SwiftUI 2.0 using UIDocumentBrowser and UIDocument?

First and foremost, this question is intended to explore the feasibility, and possible workaround, of using SwiftUI 2.0 to build production ready document-based app.

Motivation: The new document based app in SwiftUI 2.0 still lacking many features, for example,
  • DocumentGroup: almost not customizable, i.e. template selection, tint color, nav bar items.

  • FileDocument/ReferenceFileDocument: No conflict resolution whatsoever. And as far as my trails, the ReferenceFileDocument cannot trigger any write operation :(*.

Solutions: Two obvious solutions,
  1. the SwiftUI 1.0 solution is using the traditional AppDelegate, SceneDelegate, etc, then hosting the DocumentEditorView which assuming it is the SwiftUI view.

    • However, it makes develop cross iOS/macOS app harder.

    • Also harder to use the new SwiftUI stuff like AppStorage, ScenceStorage, WindowGroup, Widgets etcs. (OR there are workarounds maybe?)

  2. the SwiftUI 2.0 solution, I have actually tried two paths.

  3. A not very successful one, using #if os check to use @UIApplicationDelegateAdaptor and @NSApplicationDelegateAdaptor. (let me know if you use this path).

  4. So far partially worked, creating a SwiftUI view to wrap the DocumentBrowserViewController. see the demo code below.

My questions are:

I) Has anyone successed in using like UIDocument directly in a DocumentGroup?

II) Is DocumentGroup with "proper workaround" actually sufficient enough to replace the old framework?

III)Do you know any drawbacks to wrap a UIDocumentBrowserViewController in a SwiftUI's Representable? And if the drawback is serious enough that we should give up the SwiftUI 2.0's new App structure, and use the AppDelegate approach.

IV) Any related discussion is welcomed.


Code Block swift
struct DocumentBrowserView: View {
/// The presentation of the view itself.
@Binding var isPresented: Bool
@StateObject var model = ViewModel()
var body: some View {
Group {
#if os(iOS)
RepresentedUIDocumentBrowser()
.fullScreenCover(isPresented: .init(get: {
model.state == .loadedAndPresentingDocument
}, set: { _ in
// handle state in onDismiss.
}), onDismiss: {
model.clearDocument()
}, content: {
if model.document != nil {
EmptyView()
.environmentObject(model.document!)
} else {
EmptyView()
}
})
#elseif os(macOS)
Text("macOS NSDocumentController? Stub here.")
#else
fatalError("OS not supported.")
#endif
}
.edgesIgnoringSafeArea(.all)
}
}


Code Block swift
// MARK: - SwiftUI Interface
struct RepresentedUIDocumentBrowser {
@Binding var isPresented: Bool
@Binding var configuration: Configuration
let supportedContentTypes: [UTType]? = nil
func dismissView() {
isPresented = false
}
}
extension RepresentedUIDocumentBrowser {
public struct Configuration {
var allowsDocumentCreation = true
var allowsPickingMultipleItems = false
var shouldShowFileExtensions = false
var viewTintColor: UIColor = .systemOrange
}
}
// MARK: - UIViewControllerRepresentable
extension RepresentedUIDocumentBrowser: UIViewControllerRepresentable {
typealias UIViewControllerType = CustomUIDocumentBrowserViewController
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
func makeUIViewController(context: Context) -> CustomUIDocumentBrowserViewController {
let vc = CustomUIDocumentBrowserViewController(forOpening: supportedContentTypes)
vc.delegate = context.coordinator
updateUIViewController(vc, context: context)
return vc
}
func updateUIViewController(_ uiViewController: CustomUIDocumentBrowserViewController, context: Context) {
let vc = uiViewController
vc.allowsDocumentCreation = configuration.allowsDocumentCreation
vc.allowsPickingMultipleItems = configuration.allowsPickingMultipleItems
vc.shouldShowFileExtensions = configuration.shouldShowFileExtensions
vc.view.tintColor = configuration.viewTintColor
}
}
// MARK: - UIDocumentBrowserViewController Subclass
extension RepresentedUIDocumentBrowser {
class CustomUIDocumentBrowserViewController: UIDocumentBrowserViewController {
// Customizations
}
}
// MARK: - UIDocumentBrowserViewControllerDelegate
extension RepresentedUIDocumentBrowser {
class Coordinator: NSObject, UIDocumentBrowserViewControllerDelegate {
var parent: RepresentedUIDocumentBrowser
init(parent: RepresentedUIDocumentBrowser) {
self.parent = parent
}
// MARK: Create New
func documentBrowser(_ controller: UIDocumentBrowserViewController,
didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) {
}
func documentBrowser(_ controller: UIDocumentBrowserViewController,
didImportDocumentAt sourceURL: URL,
toDestinationURL destinationURL: URL) {
}
func documentBrowser(_ controller: UIDocumentBrowserViewController,
failedToImportDocumentAt documentURL: URL,
error: Error?) {
}
// MARK: Select
func documentBrowser(_ controller: UIDocumentBrowserViewController,
didPickDocumentsAt documentURLs: [URL]) {
}
// MARK: UIActivity
}
}

Replies

And as far as my trails, the ReferenceFileDocument cannot trigger any write operation :(*.

You have to call registerUndo on the UndoManager in the environment to trigger a save:

Code Block swift
@Environment(\.undoManager) var undoManager