So, to start with, if you're using a custom UTI, it's got to be set up correctly. Mine look like this…
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeIconFiles</key>
<array>
<string>icon-file-name</string> // Can be excluded, but keep the array
</array>
<key>CFBundleTypeName</key>
<string>Your Document Name</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.custom-uti</string>
</array>
</dict>
</array>
and
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string> // My doc is saved as Data, not a file wrapper
</array>
<key>UTTypeDescription</key>
<string>Your Document Name</string>
<key>UTTypeIdentifier</key>
<string>com.custom-uti</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>doc-extension</string>
</array>
</dict>
</dict>
</array>
Also
<key>UISupportsDocumentBrowser</key>
<true/>
I subclass UIDocument
as MyDocument
and add the following method to create a new temp document…
static func create(completion: @escaping Result<MyDocument> -> Void) throws {
let targetURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("Untitled").appendingPathExtension("doc-extension")
coordinationQueue.async {
let document = MyDocument(fileURL: targetURL)
var error: NSError? = nil
NSFileCoordinator(filePresenter: nil).coordinate(writingItemAt: targetURL, error: &error) { url in
document.save(to: url, for: .forCreating) { success in
DispatchQueue.main.async {
if success {
completion(.success(document))
} else {
completion(.failure(MyDocumentError.unableToSaveDocument))
}
}
}
}
if let error = error {
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
}
Then init and display the DBVC as follows:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
lazy var documentBrowser: UIDocumentBrowserViewController = {
let utiDecs = Bundle.main.object(forInfoDictionaryKey: kUTExportedTypeDeclarationsKey as String) as! [[String: Any]]
let uti = utiDecs.first?[kUTTypeIdentifierKey as String] as! String
let dbvc = UIDocumentBrowserViewController(forOpeningFilesWithContentTypes:[uti])
dbvc.delegate = self
return dbvc
}()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = documentBrowser
window?.makeKeyAndVisible()
return true
}
}
And my delegate methods are as follows:
func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Swift.Void) {
do {
try MyDocument.create() { result in
switch result {
case let .success(document):
// .move as I'm moving a temp file, if you're using a template
// this will be .copy
importHandler(document.fileURL, .move)
case let .failure(error):
// Show error
importHandler(nil, .none)
}
}
} catch {
// Show error
importHandler(nil, .none)
}
}
func documentBrowser(_ controller: UIDocumentBrowserViewController, didImportDocumentAt sourceURL: URL, toDestinationURL destinationURL: URL) {
let document = MyDocument(fileURL: destinationURL)
document.open { success in
if success {
// Modally present DocumentViewContoller for document
} else {
// Show error
}
}
}
And that's pretty much it. Let me know how you get on!