Fix profile save file not working
SwiftUI only supports one fileExporter modifier per view hierarchy. When multiple fileExporter modifiers are attached, only the last one takes effect. Unified ProfileExportDocument and ProfileJSONExportDocument into a single ProfileAnyExportDocument to use one fileExporter for both file types.
This commit is contained in:
@@ -104,23 +104,12 @@ public struct ProfileCard: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fileExporter(
|
.fileExporter(
|
||||||
isPresented: $viewModel.showProfileExporter,
|
isPresented: $viewModel.showExporter,
|
||||||
document: viewModel.profileExportDocument,
|
document: viewModel.exportDocument,
|
||||||
contentType: .profile,
|
contentType: viewModel.exportDocument?.contentType ?? .data,
|
||||||
defaultFilename: viewModel.profileExportDocument?.filename
|
defaultFilename: viewModel.exportDocument?.filename
|
||||||
) { result in
|
) { result in
|
||||||
viewModel.profileExportDocument = nil
|
viewModel.exportDocument = 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
|
|
||||||
if case let .failure(error) = result {
|
if case let .failure(error) = result {
|
||||||
viewModel.alert = AlertState(error: error)
|
viewModel.alert = AlertState(error: error)
|
||||||
}
|
}
|
||||||
@@ -371,18 +360,13 @@ public struct ProfileCard: View {
|
|||||||
do {
|
do {
|
||||||
switch type {
|
switch type {
|
||||||
case .file:
|
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:
|
case .json:
|
||||||
viewModel.profileJSONExportDocument = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name)
|
let doc = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name)
|
||||||
}
|
viewModel.exportDocument = ProfileAnyExportDocument(json: doc)
|
||||||
DispatchQueue.main.async {
|
|
||||||
switch type {
|
|
||||||
case .file:
|
|
||||||
viewModel.showProfileExporter = true
|
|
||||||
case .json:
|
|
||||||
viewModel.showJSONExporter = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
viewModel.showExporter = true
|
||||||
} catch {
|
} catch {
|
||||||
viewModel.alert = AlertState(error: error)
|
viewModel.alert = AlertState(error: error)
|
||||||
}
|
}
|
||||||
@@ -495,10 +479,8 @@ extension ProfileCard {
|
|||||||
@Published var profileToEdit: Profile?
|
@Published var profileToEdit: Profile?
|
||||||
@Published var shareItemType: ShareItemType?
|
@Published var shareItemType: ShareItemType?
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
@Published var profileExportDocument: ProfileExportDocument?
|
@Published var exportDocument: ProfileAnyExportDocument?
|
||||||
@Published var showProfileExporter = false
|
@Published var showExporter = false
|
||||||
@Published var profileJSONExportDocument: ProfileJSONExportDocument?
|
|
||||||
@Published var showJSONExporter = false
|
|
||||||
#endif
|
#endif
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
var shareButtonView: NSView?
|
var shareButtonView: NSView?
|
||||||
|
|||||||
@@ -532,17 +532,11 @@ private struct ProfilePickerRow: View {
|
|||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
@State private var shareItemType: ShareItemType?
|
@State private var shareItemType: ShareItemType?
|
||||||
@State private var exportItemType: ExportItemType?
|
@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?
|
@State private var menuAnchorView: NSView?
|
||||||
#endif
|
#endif
|
||||||
#if os(iOS)
|
#if !os(tvOS)
|
||||||
@State private var profileExportDocument: ProfileExportDocument?
|
@State private var exportDocument: ProfileAnyExportDocument?
|
||||||
@State private var showProfileExporter = false
|
@State private var showExporter = false
|
||||||
@State private var profileJSONExportDocument: ProfileJSONExportDocument?
|
|
||||||
@State private var showJSONExporter = false
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -722,23 +716,12 @@ private struct ProfilePickerRow: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fileExporter(
|
.fileExporter(
|
||||||
isPresented: $showProfileExporter,
|
isPresented: $showExporter,
|
||||||
document: profileExportDocument,
|
document: exportDocument,
|
||||||
contentType: .profile,
|
contentType: exportDocument?.contentType ?? .data,
|
||||||
defaultFilename: profileExportDocument?.filename
|
defaultFilename: exportDocument?.filename
|
||||||
) { result in
|
) { result in
|
||||||
profileExportDocument = nil
|
exportDocument = 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
|
|
||||||
if case let .failure(error) = result {
|
if case let .failure(error) = result {
|
||||||
alert = AlertState(error: error)
|
alert = AlertState(error: error)
|
||||||
}
|
}
|
||||||
@@ -772,23 +755,12 @@ private struct ProfilePickerRow: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fileExporter(
|
.fileExporter(
|
||||||
isPresented: $showProfileExporter,
|
isPresented: $showExporter,
|
||||||
document: profileExportDocument,
|
document: exportDocument,
|
||||||
contentType: .profile,
|
contentType: exportDocument?.contentType ?? .data,
|
||||||
defaultFilename: profileExportDocument?.filename
|
defaultFilename: exportDocument?.filename
|
||||||
) { result in
|
) { result in
|
||||||
profileExportDocument = nil
|
exportDocument = 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
|
|
||||||
if case let .failure(error) = result {
|
if case let .failure(error) = result {
|
||||||
alert = AlertState(error: error)
|
alert = AlertState(error: error)
|
||||||
}
|
}
|
||||||
@@ -985,18 +957,13 @@ private struct ProfilePickerRow: View {
|
|||||||
do {
|
do {
|
||||||
switch type {
|
switch type {
|
||||||
case .file:
|
case .file:
|
||||||
profileExportDocument = try ProfileExportDocument(content: profile.origin.toContent())
|
let doc = try ProfileExportDocument(content: profile.origin.toContent())
|
||||||
|
exportDocument = ProfileAnyExportDocument(profile: doc)
|
||||||
case .json:
|
case .json:
|
||||||
profileJSONExportDocument = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name)
|
let doc = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name)
|
||||||
}
|
exportDocument = ProfileAnyExportDocument(json: doc)
|
||||||
DispatchQueue.main.async {
|
|
||||||
switch type {
|
|
||||||
case .file:
|
|
||||||
showProfileExporter = true
|
|
||||||
case .json:
|
|
||||||
showJSONExporter = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
showExporter = true
|
||||||
} catch {
|
} catch {
|
||||||
alert = AlertState(error: error)
|
alert = AlertState(error: error)
|
||||||
}
|
}
|
||||||
@@ -1052,12 +1019,13 @@ private struct ProfilePickerRow: View {
|
|||||||
do {
|
do {
|
||||||
switch type {
|
switch type {
|
||||||
case .file:
|
case .file:
|
||||||
profileExportDocument = try ProfileExportDocument(content: profile.origin.toContent())
|
let doc = try ProfileExportDocument(content: profile.origin.toContent())
|
||||||
showProfileExporter = true
|
exportDocument = ProfileAnyExportDocument(profile: doc)
|
||||||
case .json:
|
case .json:
|
||||||
profileJSONExportDocument = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name)
|
let doc = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name)
|
||||||
showJSONExporter = true
|
exportDocument = ProfileAnyExportDocument(json: doc)
|
||||||
}
|
}
|
||||||
|
showExporter = true
|
||||||
} catch {
|
} catch {
|
||||||
alert = AlertState(error: error)
|
alert = AlertState(error: error)
|
||||||
}
|
}
|
||||||
@@ -1167,10 +1135,8 @@ private struct ProfilePickerRow: View {
|
|||||||
@State private var isUpdating = false
|
@State private var isUpdating = false
|
||||||
@State private var showQRCode = false
|
@State private var showQRCode = false
|
||||||
@State private var showQRSShare = false
|
@State private var showQRSShare = false
|
||||||
@State private var profileExportDocument: ProfileExportDocument?
|
@State private var exportDocument: ProfileAnyExportDocument?
|
||||||
@State private var showProfileExporter = false
|
@State private var showExporter = false
|
||||||
@State private var profileJSONExportDocument: ProfileJSONExportDocument?
|
|
||||||
@State private var showJSONExporter = false
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
@@ -1234,23 +1200,12 @@ private struct ProfilePickerRow: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fileExporter(
|
.fileExporter(
|
||||||
isPresented: $showProfileExporter,
|
isPresented: $showExporter,
|
||||||
document: profileExportDocument,
|
document: exportDocument,
|
||||||
contentType: .profile,
|
contentType: exportDocument?.contentType ?? .data,
|
||||||
defaultFilename: profileExportDocument?.filename
|
defaultFilename: exportDocument?.filename
|
||||||
) { result in
|
) { result in
|
||||||
profileExportDocument = nil
|
exportDocument = 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
|
|
||||||
if case let .failure(error) = result {
|
if case let .failure(error) = result {
|
||||||
alert = AlertState(error: error)
|
alert = AlertState(error: error)
|
||||||
}
|
}
|
||||||
@@ -1344,18 +1299,13 @@ private struct ProfilePickerRow: View {
|
|||||||
do {
|
do {
|
||||||
switch type {
|
switch type {
|
||||||
case .file:
|
case .file:
|
||||||
profileExportDocument = try ProfileExportDocument(content: profile.origin.toContent())
|
let doc = try ProfileExportDocument(content: profile.origin.toContent())
|
||||||
|
exportDocument = ProfileAnyExportDocument(profile: doc)
|
||||||
case .json:
|
case .json:
|
||||||
profileJSONExportDocument = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name)
|
let doc = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name)
|
||||||
}
|
exportDocument = ProfileAnyExportDocument(json: doc)
|
||||||
DispatchQueue.main.async {
|
|
||||||
switch type {
|
|
||||||
case .file:
|
|
||||||
showProfileExporter = true
|
|
||||||
case .json:
|
|
||||||
showJSONExporter = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
showExporter = true
|
||||||
} catch {
|
} catch {
|
||||||
alert = AlertState(error: error)
|
alert = AlertState(error: error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public struct TypedProfile: Transferable, Codable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public extension UTType {
|
public extension UTType {
|
||||||
static var profile: UTType { .init(exportedAs: AppConfiguration.profileUTType) }
|
static let profile = UTType(exportedAs: AppConfiguration.profileUTType)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
@@ -177,4 +177,37 @@ public extension UTType {
|
|||||||
return FileWrapper(regularFileWithContents: data)
|
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
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user