Создание документа с помощью 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.

Этот подход имеет смысл, если задуматься, по нескольким причинам:

  1. Файловая система Apple (AFS). Переход к AFS означает, что копирование (почти) бесплатно.

  2. Права доступа. Копирование из Bundle всегда допустимо, и пользователь указывает местоположение для копирования.

  3. Новая парадигма браузера документов. Так как мы используем новый UIDocumentBrowserViewController Парадигма (которая возникла благодаря iOS11 и новому приложению Files), она даже обрабатывает именование (см. страницы Apple) и перемещает и размещает файлы. Нам не нужно беспокоиться о том, на каком потоке работать.

Так. Проще, проще и, наверное, лучше. Я не могу придумать причину, чтобы вручную создать все файлы (или использовать временную папку и т. Д.).

Тест на устройстве, а не в симуляторе. В ту минуту, когда я переключился на тестирование на устройстве, все просто начало работать правильно. (ПРИМЕЧАНИЕ. Может случиться так, что сбои симулятора происходят только для пользователей Sierra, таких как я.)

Другие вопросы по тегам