15

So I've been meddling with "moving" a small SwiftUI iPad app to the Mac, and I've hit a bit of a speed bump with UIDocumentPickerViewController. I have wrapped the UIDocumentPickerViewController in a UIViewControllerRepresentable like so :

struct DocumentPickerView: UIViewControllerRepresentable {
  func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
    let documentPicker = UIDocumentPickerViewController(documentTypes: [(kUTTypeImage as String)], in: .import)
    return documentPicker
  }

  func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {

  }
}

And displaying it like this:

struct ContentView: View {
@State var shows = false
var body: some View {
    Button(action: { self.shows.toggle() }) {
        Text("Select File")
    }
    .sheet(isPresented: self.$shows) {
        DocumentPickerView()
    }
  }
}

On the iPad all is working well, iPad

But when on on the Mac, the UIDocumentPickerViewControllerdoesnt show and we get this blank modal:

Mac

Anjali Shah
  • 720
  • 7
  • 21
ALex Popa
  • 339
  • 7
  • 16
  • 3
    Well if I use UIKit with Catalyst, it opens a macos file picker. When using SwiftUI, with catalyst, i get the blank view. I would expect the same behaviour for SwiftUI. – ALex Popa Nov 30 '19 at 13:29
  • I could not reproduce your issue. It shows as you are expecting here. I run into another issue selecting a file though. It does nothing and log error: `Failed to create an FPSandboxingURLWrapper for file ... Error: Error Domain=NSPOSIXErrorDomain Code=1 "couldn't issue sandbox extension com.apple.app-sandbox.read-write for '/..fileName': Operation not permitted" UserInfo={NSDescription=couldn't issue sandbox extension com.apple.app-sandbox.read-write for '/../fileName.png': Operation not permitted}`. Using `UIDocumentBrowserViewController`instead fixes my issue. – shallowThought Dec 02 '19 at 12:17
  • I'm having the same problem. This has to be a bug in SwiftUI. It should really transform the UIDocumentPickerViewController into an NSOpenPanel on macOS. Bug for sure! – jogga Dec 05 '19 at 14:25
  • @shallowThought for some reason you need to select read &write permission for user selectedDocument. Also documentBrowser i think is intended for something else – ALex Popa Dec 05 '19 at 14:27
  • Well, if you don't have read permissions outside the sandbox it's impossible to pick an arbitrary file selected by the user... but that's not the problem. The problem is that UIDocumentPickerViewController should show an NSOpenPanel when running on macOS. It works if using UIKit, but it doesn't work with SwiftUI. @ALexPopa is correct in his observation. – jogga Dec 05 '19 at 14:41
  • I just filed a bug report through the Feedback Assistant app... – jogga Dec 05 '19 at 14:42
  • Great! Let’s hope they fix it soon. – ALex Popa Dec 05 '19 at 14:45
  • 1
    If you are working with a Document-based app as I'm doing you can work around the problem by letting the UIDocumentBrowserViewController present the dialog. It's a pain... but at least it works ;-) – jogga Dec 05 '19 at 14:48
  • 1
    `let controller = UIDocumentPickerViewController(url: tempURL, in: .moveToService) controller.delegate = self if let presentedViewController = self.presentedViewController { // let the ContentView present the self.modalSelection = .save presentedViewController.present(controller, animated: true) }` – jogga Dec 05 '19 at 14:49
  • Another way i found to work around it, is to grab the rootViewController off the keyWindow, and present the the UIDocumentPickerViewController from there – ALex Popa Dec 05 '19 at 14:50
  • @shallowThought could you provide working code? With picker or browser. – John Tracid Feb 03 '20 at 18:29

2 Answers2

1

I replaced deprecated method and enabled asked entitlement for provided topic starter code and it works.

Tested with Xcode 13.3 / macOS 12.2

import UniformTypeIdentifiers
struct DocumentPickerView: UIViewControllerRepresentable {
  func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
    let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.image], asCopy: true)
    return documentPicker
  }

  func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {

  }
}

config

and demo opening standard macOS picker

enter image description here

Asperi
  • 228,894
  • 20
  • 464
  • 690
0

For some reason (I'm not aware of further specific details), the following worked for me (for Catalyst):

Instead of using in: .import

let documentPicker = UIDocumentPickerViewController(
    documentTypes: [(kUTTypeImage as String)], 
    in: .import
)

Use in: .open

let documentPicker = UIDocumentPickerViewController(
    documentTypes: [(kUTTypeImage as String)], 
    in: .open
)

I haven't checked if the functionality is maintained in iOS, but perhaps you can use some sort of "flag" to determine wether to use .import or .open