Создание документа с помощью UIDocumentBrowserViewController
Документация для documentBrowser(_:didRequestDocumentCreationWithHandler:)
говорит: "Создайте новый документ и сохраните его во временном местоположении. Если вы используете UIDocument
подкласс для создания документа, вы должны закрыть его перед вызовом importHandler
блок ".
Поэтому я создал URL файла, взяв URL для временного каталога пользователя (FileManager.default.temporaryDirectory
) и добавление имени и расширения (получение пути, например, "file:///private/var/mobile/Containers/Data/Application/C1DE454D-EA1E-4166-B137-5B43185169D8/tmp/Untitled.uti"). Но когда я звоню save(to:for:completionHandler:)
Передав этот URL, обработчик завершения никогда не перезванивается. Я также пытался использовать url(for:in:appropriateFor:create:)
передать подкаталог во временный каталог пользователя - обработчик завершения по-прежнему никогда не вызывался.
Я понимаю, что контроллер представления браузера документов управляется отдельным процессом, который имеет собственные разрешения на чтение / запись. Помимо этого, мне трудно понять, в чем проблема. Где можно временно сохранять новые документы, чтобы их мог перемещать процесс браузера документов?
Обновление: по состоянию на текущие бета-версии, теперь я вижу ошибку с доменом NSFileProviderInternalErrorDomain
и код 1
регистрация: "Читателю не разрешен доступ к URL". По крайней мере, это подтверждение того, что происходит...
3 ответа
Итак, для начала, если вы используете пользовательский UTI, он должен быть настроен правильно. Мой выглядит так...
<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>
а также
<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>
Также
<key>UISupportsDocumentBrowser</key>
<true/>
Я подкласс UIDocument
как MyDocument
и добавьте следующий метод для создания нового временного документа…
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))
}
}
}
}
Затем инициализируйте и отобразите DBVC следующим образом:
@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
}
}
И мои методы делегата следующие:
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
}
}
}
И это в значительной степени так. Дайте мне знать, как вы поживаете!
У меня была та же проблема, но потом я понял, что рекомендуемый способ - просто скопировать пакет / папку из Bundle, вот так:
func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) {
if let url = Bundle.main.url(forResource: "Your Already Created Package", withExtension: "your-package-extension") {
importHandler(url, .copy)
} else {
importHandler(nil, .none)
}
}
Чтобы уточнить, этот пакет - просто папка, которую вы создали и добавили в Xcode.
Этот подход имеет смысл, если задуматься, по нескольким причинам:
Файловая система Apple (AFS). Переход к AFS означает, что копирование (почти) бесплатно.
Права доступа. Копирование из Bundle всегда допустимо, и пользователь указывает местоположение для копирования.
Новая парадигма браузера документов. Так как мы используем новый
UIDocumentBrowserViewController
Парадигма (которая возникла благодаря iOS11 и новому приложению Files), она даже обрабатывает именование (см. страницы Apple) и перемещает и размещает файлы. Нам не нужно беспокоиться о том, на каком потоке работать.
Так. Проще, проще и, наверное, лучше. Я не могу придумать причину, чтобы вручную создать все файлы (или использовать временную папку и т. Д.).
Тест на устройстве, а не в симуляторе. В ту минуту, когда я переключился на тестирование на устройстве, все просто начало работать правильно. (ПРИМЕЧАНИЕ. Может случиться так, что сбои симулятора происходят только для пользователей Sierra, таких как я.)