diff --git a/ApplicationLibrary/Views/Dashboard/Cards/ProfileCard.swift b/ApplicationLibrary/Views/Dashboard/Cards/ProfileCard.swift index f1119ad..a43ff62 100644 --- a/ApplicationLibrary/Views/Dashboard/Cards/ProfileCard.swift +++ b/ApplicationLibrary/Views/Dashboard/Cards/ProfileCard.swift @@ -104,23 +104,12 @@ public struct ProfileCard: View { } } .fileExporter( - isPresented: $viewModel.showProfileExporter, - document: viewModel.profileExportDocument, - contentType: .profile, - defaultFilename: viewModel.profileExportDocument?.filename + isPresented: $viewModel.showExporter, + document: viewModel.exportDocument, + contentType: viewModel.exportDocument?.contentType ?? .data, + defaultFilename: viewModel.exportDocument?.filename ) { result in - viewModel.profileExportDocument = nil - if case let .failure(error) = result { - viewModel.alert = AlertState(error: error) - } - } - .fileExporter( - isPresented: $viewModel.showJSONExporter, - document: viewModel.profileJSONExportDocument, - contentType: .json, - defaultFilename: viewModel.profileJSONExportDocument?.filename - ) { result in - viewModel.profileJSONExportDocument = nil + viewModel.exportDocument = nil if case let .failure(error) = result { viewModel.alert = AlertState(error: error) } @@ -371,18 +360,13 @@ public struct ProfileCard: View { do { switch type { case .file: - viewModel.profileExportDocument = try ProfileExportDocument(content: profile.origin.toContent()) + let doc = try ProfileExportDocument(content: profile.origin.toContent()) + viewModel.exportDocument = ProfileAnyExportDocument(profile: doc) case .json: - viewModel.profileJSONExportDocument = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name) - } - DispatchQueue.main.async { - switch type { - case .file: - viewModel.showProfileExporter = true - case .json: - viewModel.showJSONExporter = true - } + let doc = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name) + viewModel.exportDocument = ProfileAnyExportDocument(json: doc) } + viewModel.showExporter = true } catch { viewModel.alert = AlertState(error: error) } @@ -495,10 +479,8 @@ extension ProfileCard { @Published var profileToEdit: Profile? @Published var shareItemType: ShareItemType? #if !os(tvOS) - @Published var profileExportDocument: ProfileExportDocument? - @Published var showProfileExporter = false - @Published var profileJSONExportDocument: ProfileJSONExportDocument? - @Published var showJSONExporter = false + @Published var exportDocument: ProfileAnyExportDocument? + @Published var showExporter = false #endif #if os(macOS) var shareButtonView: NSView? diff --git a/ApplicationLibrary/Views/Dashboard/Cards/ProfilePickerSheet.swift b/ApplicationLibrary/Views/Dashboard/Cards/ProfilePickerSheet.swift index 80d431f..73136ae 100644 --- a/ApplicationLibrary/Views/Dashboard/Cards/ProfilePickerSheet.swift +++ b/ApplicationLibrary/Views/Dashboard/Cards/ProfilePickerSheet.swift @@ -532,17 +532,11 @@ private struct ProfilePickerRow: View { #if os(macOS) @State private var shareItemType: ShareItemType? @State private var exportItemType: ExportItemType? - @State private var profileExportDocument: ProfileExportDocument? - @State private var showProfileExporter = false - @State private var profileJSONExportDocument: ProfileJSONExportDocument? - @State private var showJSONExporter = false @State private var menuAnchorView: NSView? #endif - #if os(iOS) - @State private var profileExportDocument: ProfileExportDocument? - @State private var showProfileExporter = false - @State private var profileJSONExportDocument: ProfileJSONExportDocument? - @State private var showJSONExporter = false + #if !os(tvOS) + @State private var exportDocument: ProfileAnyExportDocument? + @State private var showExporter = false #endif var body: some View { @@ -722,23 +716,12 @@ private struct ProfilePickerRow: View { } } .fileExporter( - isPresented: $showProfileExporter, - document: profileExportDocument, - contentType: .profile, - defaultFilename: profileExportDocument?.filename + isPresented: $showExporter, + document: exportDocument, + contentType: exportDocument?.contentType ?? .data, + defaultFilename: exportDocument?.filename ) { result in - profileExportDocument = nil - if case let .failure(error) = result { - alert = AlertState(error: error) - } - } - .fileExporter( - isPresented: $showJSONExporter, - document: profileJSONExportDocument, - contentType: .json, - defaultFilename: profileJSONExportDocument?.filename - ) { result in - profileJSONExportDocument = nil + exportDocument = nil if case let .failure(error) = result { alert = AlertState(error: error) } @@ -772,23 +755,12 @@ private struct ProfilePickerRow: View { } } .fileExporter( - isPresented: $showProfileExporter, - document: profileExportDocument, - contentType: .profile, - defaultFilename: profileExportDocument?.filename + isPresented: $showExporter, + document: exportDocument, + contentType: exportDocument?.contentType ?? .data, + defaultFilename: exportDocument?.filename ) { result in - profileExportDocument = nil - if case let .failure(error) = result { - alert = AlertState(error: error) - } - } - .fileExporter( - isPresented: $showJSONExporter, - document: profileJSONExportDocument, - contentType: .json, - defaultFilename: profileJSONExportDocument?.filename - ) { result in - profileJSONExportDocument = nil + exportDocument = nil if case let .failure(error) = result { alert = AlertState(error: error) } @@ -985,18 +957,13 @@ private struct ProfilePickerRow: View { do { switch type { case .file: - profileExportDocument = try ProfileExportDocument(content: profile.origin.toContent()) + let doc = try ProfileExportDocument(content: profile.origin.toContent()) + exportDocument = ProfileAnyExportDocument(profile: doc) case .json: - profileJSONExportDocument = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name) - } - DispatchQueue.main.async { - switch type { - case .file: - showProfileExporter = true - case .json: - showJSONExporter = true - } + let doc = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name) + exportDocument = ProfileAnyExportDocument(json: doc) } + showExporter = true } catch { alert = AlertState(error: error) } @@ -1052,12 +1019,13 @@ private struct ProfilePickerRow: View { do { switch type { case .file: - profileExportDocument = try ProfileExportDocument(content: profile.origin.toContent()) - showProfileExporter = true + let doc = try ProfileExportDocument(content: profile.origin.toContent()) + exportDocument = ProfileAnyExportDocument(profile: doc) case .json: - profileJSONExportDocument = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name) - showJSONExporter = true + let doc = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name) + exportDocument = ProfileAnyExportDocument(json: doc) } + showExporter = true } catch { alert = AlertState(error: error) } @@ -1167,10 +1135,8 @@ private struct ProfilePickerRow: View { @State private var isUpdating = false @State private var showQRCode = false @State private var showQRSShare = false - @State private var profileExportDocument: ProfileExportDocument? - @State private var showProfileExporter = false - @State private var profileJSONExportDocument: ProfileJSONExportDocument? - @State private var showJSONExporter = false + @State private var exportDocument: ProfileAnyExportDocument? + @State private var showExporter = false var body: some View { Group { @@ -1234,23 +1200,12 @@ private struct ProfilePickerRow: View { } } .fileExporter( - isPresented: $showProfileExporter, - document: profileExportDocument, - contentType: .profile, - defaultFilename: profileExportDocument?.filename + isPresented: $showExporter, + document: exportDocument, + contentType: exportDocument?.contentType ?? .data, + defaultFilename: exportDocument?.filename ) { result in - profileExportDocument = nil - if case let .failure(error) = result { - alert = AlertState(error: error) - } - } - .fileExporter( - isPresented: $showJSONExporter, - document: profileJSONExportDocument, - contentType: .json, - defaultFilename: profileJSONExportDocument?.filename - ) { result in - profileJSONExportDocument = nil + exportDocument = nil if case let .failure(error) = result { alert = AlertState(error: error) } @@ -1344,18 +1299,13 @@ private struct ProfilePickerRow: View { do { switch type { case .file: - profileExportDocument = try ProfileExportDocument(content: profile.origin.toContent()) + let doc = try ProfileExportDocument(content: profile.origin.toContent()) + exportDocument = ProfileAnyExportDocument(profile: doc) case .json: - profileJSONExportDocument = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name) - } - DispatchQueue.main.async { - switch type { - case .file: - showProfileExporter = true - case .json: - showJSONExporter = true - } + let doc = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name) + exportDocument = ProfileAnyExportDocument(json: doc) } + showExporter = true } catch { alert = AlertState(error: error) } diff --git a/Library/Database/Profile+Transferable.swift b/Library/Database/Profile+Transferable.swift index b9cd5b6..1569a4a 100644 --- a/Library/Database/Profile+Transferable.swift +++ b/Library/Database/Profile+Transferable.swift @@ -115,7 +115,7 @@ public struct TypedProfile: Transferable, Codable { } public extension UTType { - static var profile: UTType { .init(exportedAs: AppConfiguration.profileUTType) } + static let profile = UTType(exportedAs: AppConfiguration.profileUTType) } #if !os(tvOS) @@ -177,4 +177,37 @@ public extension UTType { return FileWrapper(regularFileWithContents: data) } } + + public struct ProfileAnyExportDocument: FileDocument { + public static var readableContentTypes: [UTType] { [.data, .json] } + + public let data: Data + public let filename: String + public let contentType: UTType + + public init(profile: ProfileExportDocument) { + data = profile.data + filename = profile.filename + contentType = .data + } + + public init(json: ProfileJSONExportDocument) { + data = json.content.data(using: .utf8) ?? Data() + filename = json.filename + contentType = .json + } + + public init(configuration: ReadConfiguration) throws { + guard let data = configuration.file.regularFileContents else { + throw CocoaError(.fileReadCorruptFile) + } + self.data = data + filename = "profile" + contentType = .data + } + + public func fileWrapper(configuration _: WriteConfiguration) throws -> FileWrapper { + FileWrapper(regularFileWithContents: data) + } + } #endif