I've found very interesting behavior. So, here is a view with a button to open the file importer. If it is put in any view, it will work fine without issue. However, if it is put into the commands, suddenly it crashes the application.
Anybody know why?
Note: I'm working only on macOS application.
File importer button source:
import SwiftUI
struct FileImporterButtonView: View {
@State var isPresented = false
var body: some View {
Button("Open...") {
isPresented.toggle()
}
.keyboardShortcut("O")
.fileImporter(isPresented: $isPresented, allowedContentTypes: [.item], allowsMultipleSelection: false, onCompletion: { _ in
print("just tried to open file importer...")
})
}
}
Content view source:
import SwiftUI
struct ContentView: View {
var body: some View {
FileImporterButtonView() // File importer works fine
}
}
Application source:
import SwiftUI
@main
struct fileImporterCrashApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}.commands {
CommandGroup(before: CommandGroupPlacement.newItem) {
FileImporterButtonView() // This leads to crash, why???
}
}
}
}
Crash info:
SwiftUI`___lldb_unnamed_symbol194403:
0x1b3f45dec <+0>: pacibsp
0x1b3f45df0 <+4>: stp x22, x21, [sp, #-0x30]!
0x1b3f45df4 <+8>: stp x20, x19, [sp, #0x10]
0x1b3f45df8 <+12>: stp x29, x30, [sp, #0x20]
0x1b3f45dfc <+16>: add x29, sp, #0x20
0x1b3f45e00 <+20>: mov x19, x8
0x1b3f45e04 <+24>: adrp x8, 180875
0x1b3f45e08 <+28>: ldr x8, [x8, #0x390]
0x1b3f45e0c <+32>: add x21, x20, x8
0x1b3f45e10 <+36>: mov x0, x21
0x1b3f45e14 <+40>: bl 0x1b4786850 ; symbol stub for: swift_unknownObjectWeakLoadStrong
0x1b3f45e18 <+44>: cbz x0, 0x1b3f45e7c ; <+144>
0x1b3f45e1c <+48>: mov x20, x0
0x1b3f45e20 <+52>: ldr x21, [x21, #0x8]
0x1b3f45e24 <+56>: bl 0x1b4786470 ; symbol stub for: swift_getObjectType
0x1b3f45e28 <+60>: mov x8, x21
0x1b3f45e2c <+64>: ldr x9, [x8, #0x10]!
0x1b3f45e30 <+68>: mov x1, x21
0x1b3f45e34 <+72>: mov x17, x8
0x1b3f45e38 <+76>: movk x17, #0x6cba, lsl #48
0x1b3f45e3c <+80>: blraa x9, x17
0x1b3f45e40 <+84>: mov x21, x0
0x1b3f45e44 <+88>: mov x0, x20
0x1b3f45e48 <+92>: bl 0x1b4785e50 ; symbol stub for: objc_release
0x1b3f45e4c <+96>: mov x8, x19
0x1b3f45e50 <+100>: mov x20, x21
0x1b3f45e54 <+104>: bl 0x1b43e1774 ; ___lldb_unnamed_symbol225352
0x1b3f45e58 <+108>: mov x0, x21
0x1b3f45e5c <+112>: ldp x29, x30, [sp, #0x20]
0x1b3f45e60 <+116>: ldp x20, x19, [sp, #0x10]
0x1b3f45e64 <+120>: ldp x22, x21, [sp], #0x30
0x1b3f45e68 <+124>: autibsp
0x1b3f45e6c <+128>: eor x16, x30, x30, lsl #1
0x1b3f45e70 <+132>: tbz x16, #0x3e, 0x1b3f45e78 ; <+140>
0x1b3f45e74 <+136>: brk #0xc471
0x1b3f45e78 <+140>: b 0x1b4786630 ; symbol stub for: swift_release
-> 0x1b3f45e7c <+144>: brk #0x1
I think I managed to find a working solution for now.
From my investigation, of the crash, it looks like SwiftUI trying to access some dead object.
My assumption is: button from the top bar menu is destroyed after click, so the context of file importer is no longer valid, but it is just an assumption.
Solution: create some invisible container which exists whole program runtime.
File importer container:
import SwiftUI
struct FileImporterContainer: View {
@Binding var isPresented: Bool
var body: some View {
Rectangle()
.frame(width: 0, height: 0)
.fileImporter(isPresented: $isPresented, allowedContentTypes: [.item], allowsMultipleSelection: false, onCompletion: { _ in
print("just tried to open file importer...")
})
}
}
Application source:
import SwiftUI
@main
struct testApp: App {
@State var isPresented = false
var body: some Scene {
WindowGroup {
FileImporterContainer(isPresented: $isPresented)
ContentView()
}.commands {
CommandGroup(before: CommandGroupPlacement.newItem) {
Button("Open...") {
isPresented.toggle()
}
.keyboardShortcut("O")
}
}
}
}
Note: this is definitely not the best solution; at least it works.
Thanks Frank-Peter Poschmann article which gave me this solution: https://fpposchmann.de/fileexporter-and-fileimporter-wont-work-inside-a-menu-in-swiftui/