Я пытался использовать файл 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
}
}