'Can't select a folder with UIViewRepresentable & UIDocumentPicker

I'm trying to create a picker for a user to choose a folder in a SwiftUI app. However, there doesn't seem to be any SwiftUI document picker yet so I was attempting to use UIViewRepresentable to display a document picker using the folder document picker outlined here: https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories. However, as seen in the image below I'm not actually able to select the folder in any way - am I missing specific to use the picker with SwiftUI?

limit

FolderPicker code:

struct FolderPicker: UIViewControllerRepresentable {
    @Binding var folderURL: String?
    
    func makeCoordinator() -> Coordinator {
        return FolderPicker.Coordinator(parent: self)
    }
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<FolderPicker>) -> UIDocumentPickerViewController {
        let picker = UIDocumentPickerViewController(documentTypes: ["kUTTypeFolder"], in: .import)
        picker.delegate = context.coordinator
        return picker
    }
    
    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<FolderPicker>) {}
    
    class Coordinator: NSObject, UIDocumentPickerDelegate {
        var parent: FolderPicker
        
        init(parent: FolderPicker) {
            self.parent = parent
        }
        
        internal func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL){
            print(url)
            parent.folderURL = url.absoluteString
        }
    }
}

which is in a TestView:

struct TestView: View {
    @State var displayPicker = false

    var body: some View {
        Button(action: {displayPicker.toggle}, label: "toggle")
            .sheet(isPresented: $showPicker) {
                FolderPicker(folderURL: $url)
            }
    }
}


Solution 1:[1]

kUTTypeFolder is not a string.

The correct way is


let picker = UIDocumentPickerViewController(documentTypes: [kUTTypeFolder as String], in: .import)

The constant kUTTypeFolder comes from import CoreServices.

Also, public init(documentTypes allowedUTIs: [String], in mode: UIDocumentPickerMode) and optional func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL)is deprecated

So use, public convenience init(forOpeningContentTypes contentTypes: [UTType]) and optional func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL])

Final code is :

struct FolderPicker: UIViewControllerRepresentable {
    
    @Binding var folderURL: String?
    
    func makeCoordinator() -> Coordinator {
        return FolderPicker.Coordinator(parent: self)
    }
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<FolderPicker>) -> UIDocumentPickerViewController {
        let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.folder])
        picker.delegate = context.coordinator
        return picker
    }
    
    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<FolderPicker>) {}
    
    class Coordinator: NSObject, UIDocumentPickerDelegate {
        var parent: FolderPicker
        
        init(parent: FolderPicker) {
            self.parent = parent
        }
        func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
            guard let url = urls.first else {
                return
            }
            print(url)
            parent.folderURL = url.absoluteString
        }
    }
}

Solution 2:[2]

Use .fileImporter presentation modifire (above ios 14)

        .fileImporter(isPresented: $showDocumentPicker,
                      allowedContentTypes: [.folder],
                      allowsMultipleSelection: true)
        { result in

         // processing results Result<[URL], Error>
        
        }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2