I have an import (and export) button(s) that works fine placed in a view.
var body: some View {
Button(action: {
isImporting = true
}, label: {
Label("Import", systemImage: "square.and.arrow.down")
})
.fileImporter(
isPresented: $isImporting,
allowedContentTypes: [UTType.log, UTType.text, UTType.xml, UTType.zip],
allowsMultipleSelection: true
) { result in
switch result {
case let .success(files):
files.forEach { file in
guard file.startAccessingSecurityScopedResource() else { return }
.../...
file.stopAccessingSecurityScopedResource()
}
case let .failure(error):
print(error)
}
}
}
But I also want import/export commands available via the File menu in the macOS application.
struct ImportExportCommands: Commands {
@MainActor
var body: some Commands {
CommandGroup(replacing: .importExport) {
Section {
ImportButton()
.modelContainer(mdContainer.sharedModelContainer)
ExportButton()
.modelContainer(mdContainer.sharedModelContainer)
}
}
}
}
The action code is executed, but no file dialog appears.
I didn't find anything about this so I suppose that is a very stupid thing.
How can I have fileImporter/fileExporter dialog pop up on the screen from a menu command?
I had this problem for a while and never find anything about it. But finally I have found this page:
".fileExporter and .fileImporter Won’t Work inside a Menu in SwiftUI" https://fpposchmann.de/fileexporter-and-fileimporter-wont-work-inside-a-menu-in-swiftui/ Thanks to Frank-Peter for the idea.
I have reworked my code and now commands in the CommandGroup works as it should.
private struct FactorizedIExportButton: View {
@Binding var isExporting: Bool
var body: some View {
Button(action: {
isExporting = true
}, label: {
Label("Export…", systemImage: "square.and.arrow.up")
})
.keyboardShortcut("E")
}
}
struct ImportButton: View {
@State var isImporting: Bool = false
var body: some View {
FactorizedImportButton(isImporting: $isImporting)
.fileImporter(
isPresented: $isImporting,
allowedContentTypes: [.text, .kmz],
allowsMultipleSelection: true
) { result in importFiles(result) }
}
}
struct ExportButton: View {
@State var isExporting: Bool = false
var document = TextDocument()
var body: some View {
FactorizedIExportButton(isExporting: $isExporting)
.fileExporter(
isPresented: $isExporting,
document: document,
contentTypes: [.gpx],
defaultFilename: "Export"
) { result in exportFile(result) }
}
}
struct ImportExportCommands: Commands {
@State var isImporting: Bool = false
@State var isExporting: Bool = false
var document = TextDocument()
@MainActor
var body: some Commands {
CommandGroup(replacing: .importExport) {
Section {
FactorizedImportButton(isImporting: $isImporting)
FactorizedIExportButton(isExporting: $isExporting)
}
.fileImporter(
isPresented: $isImporting,
allowedContentTypes: [.text, .kmz],
allowsMultipleSelection: true
) { result in importFiles(result) }
.fileExporter(
isPresented: $isExporting,
document: document,
contentTypes: [.gpx],
defaultFilename: "Export"
) { result in exportFile(result) }
}
}
}