How to popup a document picker in ios and macos using catalyst

uidocumentpickerviewcontroller
uidocumentpickerviewcontroller save file
uidocumentpickerviewcontroller catalyst
uidocumentpickerviewcontroller ios 13
uidocumentpickerviewcontroller subclass
uidocumentpickerviewcontroller ios 11
uidocumentpickerviewcontroller example
document picker swift github

I'm trying to popup a document picker using mac catalyst, but all I get is a blank screen.

All works well on ios 13.2.2 on iPad and iPhone, but not on macos 10.15.1 catalina.

Does anyone knows how to popup a document picker in both ios and macos using catalyst?

with my entitlements file having:

<key>com.apple.security.app-sandbox</key>
<false/>

Here is the test code that shows the problem.

import Foundation
import SwiftUI

struct ContentView: View {
@State var isFilePickerShown = false

var body: some View {
    VStack {
        Button(action: { self.isFilePickerShown.toggle() }) {
            Image(systemName: "rectangle.and.paperclip").resizable().frame(width: 70, height: 70)
        }
    }.sheet(isPresented: $isFilePickerShown, onDismiss: {self.isFilePickerShown = false}) {
        DocPickerViewController(callback: self.filePicked, onDismiss: { self.isFilePickerShown = false })
    }
}

func filePicked(_ url: URL) {
    print("\nThe url is: \(url)")
}

}

struct DocPickerViewController: UIViewControllerRepresentable {

private let docTypes: [String] = ["com.adobe.pdf", "public.text", "public.composite-content"]
var callback: (URL) -> ()
private let onDismiss: () -> Void

init(callback: @escaping (URL) -> (), onDismiss: @escaping () -> Void) {
    self.callback = callback
    self.onDismiss = onDismiss
}

func makeCoordinator() -> Coordinator { Coordinator(self) }

func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<DocPickerViewController>) {
}

func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
    let controller = UIDocumentPickerViewController(documentTypes: docTypes, in: .import)
    controller.allowsMultipleSelection = false
    controller.delegate = context.coordinator
    return controller
}

class Coordinator: NSObject, UIDocumentPickerDelegate {
    var parent: DocPickerViewController
    init(_ pickerController: DocPickerViewController) {
        self.parent = pickerController
    }
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        parent.callback(urls[0])
        parent.onDismiss()
    }
    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
        parent.onDismiss()
    }
}
}

I got something going with the following code. But really this is a messy business and I feel this is not the answer.

Now I have the same problem trying to display a UIActivityViewController. I can display it on Mac using the same approach but I can't control where it shows up. It's always at (0,0).

struct FilePicker: UIViewControllerRepresentable {

private let docTypes: [String] = ["com.adobe.pdf", "public.text", "public.composite-content"]
private let controller: FilePickerController?

var callback: (URL) -> ()

private let onDismiss: () -> Void

init(callback: @escaping (URL) -> (), onDismiss: @escaping () -> Void) {
    self.callback = callback
    self.onDismiss = onDismiss
    self.controller = FilePickerController(documentTypes: docTypes, mode: .import)
}

func makeCoordinator() -> Coordinator {
    return Coordinator(self)
}

func updateUIViewController(_ uiViewController: FilePickerController, context: UIViewControllerRepresentableContext<FilePicker>) { }

func makeUIViewController(context: Context) -> FilePickerController {
    return controller!
}

class Coordinator: NSObject, FilePickerControllerDelegate {
    var parent: FilePicker

    init(_ filePicker: FilePicker) {
        self.parent = filePicker
        super.init()
        self.parent.controller?.delegate = self
    }

    func documentPicker(_ controller: FilePickerController, didPickDocumentsAt urls: [URL]) {
        parent.callback(urls[0])
        parent.onDismiss()
    }

    func documentPickerWasCancelled(_ controller: FilePickerController) {
        parent.onDismiss()
    }
}
}


protocol FilePickerControllerDelegate: class {
func documentPickerWasCancelled(_ controller: FilePickerController)
func documentPicker(_ controller: FilePickerController, 
didPickDocumentsAt urls: [URL])
}

class FilePickerController: UIViewController, UIDocumentPickerDelegate {

weak var delegate: FilePickerControllerDelegate?

let viewController: UIDocumentPickerViewController?

public init(documentTypes: [String], mode: UIDocumentPickerMode) {
    viewController = UIDocumentPickerViewController(documentTypes: documentTypes, in: mode)
    super.init(nibName: nil, bundle: nil)
}

required public init?(coder: NSCoder) {
    viewController = UIDocumentPickerViewController(coder: coder)
    super.init(coder: coder)
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    if let viewController = viewController {
        viewController.delegate = self
        self.present(viewController, animated: animated)
    }
}

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    self.dismiss(animated: false) {
        self.delegate?.documentPicker(self, didPickDocumentsAt: urls)
    }
}

func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
    self.dismiss(animated: false) {
        self.delegate?.documentPickerWasCancelled(self)
    }
}

}

I call it like this:

.sheet(isPresented: $isShown, onDismiss: {self.isShown = false}) {
        #if targetEnvironment(macCatalyst)
            FilePicker(callback: self.filePicked, onDismiss: { self.isShown = false })
        #else
            DocPickerViewController(callback: self.filePicked, onDismiss: { self.isShown = false })
        #endif
    }

Workaround: present picker controller for catalyst

UIApplication.shared.firstKeyWindow?.rootViewController!.present(self.picker.controller!, animated: true)

extension UIApplication {
    public var firstKeyWindow: UIWindow? {
        windows.first(where: { $0.isKeyWindow })
    }
}

UIDocumentPickerViewController not…, In this Catalyst tutorial, you'll learn how to take an iPad app and configure it to You might have seen this project before if you did the Document Based Apps tutorial. Build and run the project in the Markup-Starter folder using the iOS In this section, you'll shift the color selector, image picker and share� You create your own style or use one of the styles provided by SwiftUI, like Segmented Picker Style or Pop Up Button Picker Style. To set a specific style for all picker instances within a view, use the picker Style(_:) modifier. The following example adds a second binding type, Topping, and applies the Segmented Picker Style to two pickers:


For all with code from hstdt and workingdog.

import SwiftUI

struct ContentView: View {
    @State private var isFilePickerShown = false
    @State private var picker = DocumentPicker()

    var body: some View {
        VStack {
            Button(action: {
                self.isFilePickerShown.toggle()
                 #if targetEnvironment(macCatalyst)
                UIApplication.shared.windows[0].rootViewController!.present(self.picker.viewController, animated: true)
                #endif
            }) {
                Image(systemName: "rectangle.and.paperclip").resizable().frame(width: 70, height: 70)
            }
        }
        .sheet(isPresented: $isFilePickerShown, onDismiss: {self.isFilePickerShown = false}) {
            DocPickerViewController(callback: self.filePicked, onDismiss: { self.isFilePickerShown = false })
        }
    }

    func filePicked(_ url: URL) {
        print("\nThe url is: \(url)")
    }
}

and now create new swiftui file DocumentPicker for MacOS:

import SwiftUI

final class DocumentPicker: NSObject, UIViewControllerRepresentable {
    typealias UIViewControllerType = UIDocumentPickerViewController

    lazy var viewController:UIDocumentPickerViewController = {
        // For picked only folder
        let vc = UIDocumentPickerViewController(documentTypes: ["public.folder"], in: .open)
        // For picked every document
//        let vc = UIDocumentPickerViewController(documentTypes: ["public.data"], in: .open)
        // For picked only images
//        let vc = UIDocumentPickerViewController(documentTypes: ["public.image"], in: .open)
        vc.allowsMultipleSelection = false
//        vc.accessibilityElements = [kFolderActionCode]
//        vc.shouldShowFileExtensions = true
        vc.delegate = self
        return vc
    }()

    func makeUIViewController(context: UIViewControllerRepresentableContext<DocumentPicker>) -> UIDocumentPickerViewController {
        viewController.delegate = self
        return viewController
    }

    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<DocumentPicker>) {
    }
}

extension DocumentPicker: UIDocumentPickerDelegate {
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        print(urls)
    }

    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
        controller.dismiss(animated: true) {
        }
        print("cancelled")
    }
}

This code popup a document picker in both iOS and MacOS using catalyst. With MacOS Catalyst work with DocumentPicker Class and with iOS with DocPickerViewController write by workingdog (see workingdog post above)

Catalyst Tutorial: Running iPad apps on macOS, 有谁知道如何使用催化剂在ios和macos中弹出文档选择器我的权利文件 How to popup a document picker in ios and macos using catalyst. Congratulations! You added features to both macOS and iOS with one set of project changes. This is a concrete example of the benefits of the Catalyst framework. Mac Exclusive Features. macOS has several features that iOS doesn’t. Windows can have toolbars at the top for useful UI.


That's the example from Cesare Piersigilli on GitHub: https://github.com/AndreasPrang/Catalyst-File-Picker

ios, Fixes a problem where signing a document with existing digital signatures may invalidate them. Improves compatibility with Mac Catalyst on macOS Big Sur. Fixes a “does not recognize selector” crash that would occur on older iOS 13.4 betas that Fixes an issue where popup annotations were positioned incorrectly. 1 How to popup a document picker in ios and macos using catalyst Feb 1. 1 UIDocumentPickerViewController with Catalyst on MACOS Feb 13.


Changelog, (You can use Quick Look in any view.) This way you can browse through a number of documents with names and icon previews that may look� 4 How to popup a document picker in ios and macos using catalyst Nov 9 '19. 3 calling an API when date changed in DatePicker SwiftUI Apr 6. 3 SwiftUI:


Master the macOS Open and Save Dialogs, When we released the first set of apps using Catalyst [in Mojave], iOS apps on the Mac [in the Catalina developer beta] are starting to feel like a pretty sweet solution. There are no APIs for making standard pop-up menus or table views. That date-picker design should not ever appear on macOS,� The wheel isn't what I would want on the Mac anyhow, so I've tried using: #if os(iOS) private let pickerStyle = WheelPickerStyle() #else private let pickerStyle = PopUpButtonPickerStyle() #endif. This seems to have no effect at all, however. On the Mac still drawn like a wheel picker and still inert.


Blog - Catalyst in Catalina, All the major iOS developer and API changes announced at WWDC19. Note: Shipping iOS apps on macOS using Project Catalyst requires macOS 10.15. Apple's Notes app has a neat document scanning system built in, and now How to let users choose a font with UIFontPickerViewController � How� Pickers. A picker can display one or more scrollable lists of distinct values from which people can choose. In iOS 14 and later, a date picker supports additional ways to choose values, like selecting a day in a calendar view or entering dates and times using a numeric keypad (for guidance, see Date Pickers).