Файл области как UIDocument - PullRequest
       28

Файл области как UIDocument

0 голосов
/ 07 апреля 2020

Я пытался использовать файл Realm в качестве документа в приложении iOS на основе документов. Цель состоит в том, чтобы выбрать файл Realm из средства выбора файлов, чтобы пользователь мог переключаться между базами данных Realm или отправлять базу данных, с которой он работал, другому пользователю. Подобно текстовому редактору, создающему новый текстовый файл для каждого нового документа, который создает пользователь, но с использованием базы данных Realm вместо текстового файла.

У меня средство выбора файлов отображается в качестве начального представления просто отлично (поэтому я думаю, что DocumentBrowswerViewController не является root проблемы), но кнопка «+» для создания нового файла фактически не создает новый файл. Чего мне не хватает, чтобы создать пустой файл (с файлом default.realm, который я включил в комплект приложения) или открыть существующий файл?

Мой DocumentBrowserViewController класс:

class DocumentBrowserViewController: UIDocumentBrowserViewController, UIDocumentBrowserViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        delegate = self

        allowsDocumentCreation = true
        allowsPickingMultipleItems = false
    }

    func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) {
        let newDocumentURL: URL? = Bundle.main.url(forResource: "default", withExtension: "realm")

        if newDocumentURL != nil {
            importHandler(newDocumentURL, .copy)
        } else {
            importHandler(nil, .none)
        }
    }

    func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) {
        guard let sourceURL = documentURLs.first else { return }

        presentDocument(at: sourceURL)
    }

    func documentBrowser(_ controller: UIDocumentBrowserViewController, didImportDocumentAt sourceURL: URL, toDestinationURL destinationURL: URL) {
        presentDocument(at: destinationURL)
    }

    func documentBrowser(_ controller: UIDocumentBrowserViewController, failedToImportDocumentAt documentURL: URL, error: Error?) {
        // Make sure to handle the failed import appropriately, e.g., by presenting an error message to the user.
    }

    // MARK: Document Presentation

    func presentDocument(at documentURL: URL) {
        let document = try! Document(contentsOf: documentURL, ofType: "RealmDocument")

        // Access the document
        document.open(completionHandler: { success in
            if success {
                // Display the content of the document:
                let view = RootView(realmDocument: document) 

                let documentViewController = UIHostingController(rootView: view)
                self.present(documentViewController, animated: true, completion: nil)
            } else {
                // Make sure to handle the failed import appropriately, e.g., by presenting an error message to the user.
            }
        })
    }

    func closeDocument(_ document: Document) {
        dismiss(animated: true) {
            document.close(completionHandler: nil)
        }
    }
}

и класс Document (перенос файла базы данных Realm):

enum RealmDocumentError: Error {
    case FailedToConvertURLToFilePath
}

class Document: UIDocument {

    var representedRealm: Realm = {
        var config = Realm.Configuration()
        config.inMemoryIdentifier = NSUUID().uuidString
        do {
            return try Realm(configuration:config)
        } catch {
            fatalError("Realm init failed")
        }
    }()


    private static func realmURL(url: URL) -> URL {
        return url.appendingPathComponent(NSUUID().uuidString)
    }


    convenience init(contentsOf url: URL, ofType typeName: String) throws {
        self.init()

        do {
            try representedRealm = Realm(fileURL: url)
        } catch {
            fatalError("Something went wrong creating the realm for \(url)")
        }
    }


    convenience init(for urlOrNil: URL?, withContentsOfURL contentsURL: URL, ofType typeName: String) throws {
        self.init()

        var config = Realm.Configuration()
        config.fileURL = contentsURL
        config.readOnly = true
        let originalRealm = try Realm(configuration: config)

        if let url = urlOrNil {
            try originalRealm.writeCopy(toFile: url)
            representedRealm = try Realm(fileURL: url)
        } else {
            var temporaryInMemoryRealmURL = URL(fileURLWithPath:NSTemporaryDirectory())
            temporaryInMemoryRealmURL.appendPathComponent(NSUUID().uuidString)

            try originalRealm.writeCopy(toFile: temporaryInMemoryRealmURL)
            representedRealm = try Realm(fileURL: temporaryInMemoryRealmURL)
        }
    }

    deinit {
        // This is a workaround — ideally Realm would allow initialising an in-memory Realm based on an on-disk representation and this cleanup code would be unnecessary
        guard let url = representedRealm.configuration.fileURL else {
            return
        }
        if url.path.hasPrefix(NSTemporaryDirectory()) {
            do {
                let fileManager = FileManager.default
                try fileManager.removeItem(at: url)
            }   catch let error as NSError {
                Swift.print("Error while deleting temporary Realm: \(error.localizedDescription)")
            }
        }
    }

    override func load(fromContents contents: Any, ofType typeName: String?) throws {
        let url = fileURL
        let proposedRealmPath = url.path
        if let currentRealmPath = representedRealm.configuration.fileURL?.path {

            if currentRealmPath != proposedRealmPath {
                try! representedRealm.writeCopy(toFile: url)
                var config = Realm.Configuration()
                config.fileURL = url
                representedRealm = try! Realm(configuration: config)

                let fileManager = FileManager.default
                try! fileManager.removeItem(atPath: currentRealmPath)
            }
        } else {
            throw RealmDocumentError.FailedToConvertURLToFilePath
        }
    }


    override func writeContents(_ contents: Any, to url: URL, for saveOperation: UIDocument.SaveOperation, originalContentsURL: URL?) throws {
        try representedRealm.writeCopy(toFile: url)
    }


    override var hasUnsavedChanges: Bool {
        return representedRealm.isInWriteTransaction
    }

}
...