swiftxcodemacosswiftuiswiftui-fileimporter

SwiftUI: fileImporter crashes application from Commands menu


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:

enter image description here

enter image description here

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

Solution

  • 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/