如何为NSPersistentContainer设置自定义商店网址

如何为NSPersistentContainer设置自定义商店网址

问题描述:

如何将自定义store.sqlite网址设置为NSPersistentContainer?如何为NSPersistentContainer设置自定义商店网址

我发现了一个丑陋的方式,继承NSPersistentContainer:

final public class PersistentContainer: NSPersistentContainer { 
private static var customUrl: URL? 

public init(name: String, managedObjectModel model: NSManagedObjectModel, customStoreDirectory baseUrl:URL?) { 
    super.init(name: name, managedObjectModel: model) 
    PersistentContainer.customUrl = baseUrl 
} 

override public class func defaultDirectoryURL() -> URL { 
    return (customUrl != nil) ? customUrl! : super.defaultDirectoryURL() 
} 

}

有没有好方法?

背景:我需要保存到应用程序组共享目录。

您可以使用NSPersistentStoreDescription类来做到这一点。它有一个初始化程序,您可以使用该初始化程序来提供持久存储文件应该放到的文件URL。

let description = NSPersistentStoreDescription(url: myURL) 

然后,使用NSPersistentContainerpersistentStoreDescriptions属性来告诉它使用这个自定义位置。

container.persistentStoreDescriptions = [description] 

注:myURL必须提供完整的/path/to/model.sqlite,即使不存在了。它不会仅用于设置父目录。

+0

这似乎只适用于,如果store.sqlite已经存在,而不是初始化CoreData堆栈。 – shallowThought

+0

不,它工作正常,如果该文件不存在。但是,您想要文件的目录必须存在,并且您的应用程序组需要正确设置。不过,没有要求存储文件已经存在。 –

+0

奇怪。它与“找不到文件”错误在这里崩溃。如果文件已经存在,可以正常工作。 – shallowThought

扩大汤姆的答案,当您使用NSPersistentStoreDescription用于任何目的,一定要与NSPersistentStoreDescription(url:)来初始化,因为在我的经验,如果你使用基本的初始化NSPersistentStoreDescription()loadPersistentStores()基于这样的描述,它会覆盖现有的持久性存储和所有它的数据在您下次构建和运行时。下面是我使用的设置URL和说明代码:

let container = NSPersistentContainer(name: "MyApp") 

let storeDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first! 
let url = storeDirectory.appendingPathComponent("MyApp.sqlite") 
let description = NSPersistentStoreDescription(url: url) 
description.shouldInferMappingModelAutomatically = true 
description.shouldMigrateStoreAutomatically = true 
container.persistentStoreDescriptions = [description] 

container.loadPersistentStores { (storeDescription, error) in 
    if let error = error as? NSError { 
     print("Unresolved error: \(error), \(error.userInfo)") 
    } 
} 

我刚刚发现,由PersistentContainer创建数据库的位置是从数据库通过UIManagedDocument创建不同的。下面是UIManagedDocument分贝位置的快照:

enter image description here

和下面的代码用于创建数据库:

let fileURL = db.fileURL // url to ".../Documents/defaultDatabase" 
let fileExist = FileManager.default.fileExists(atPath: fileURL.path) 
if fileExist { 
    let state = db.documentState 
    if state.contains(UIDocumentState.closed) { 
     db.open() 
    } 
} else { 
    // Create database 
    db.save(to: fileURL, for:.forCreating) 
} 

它看起来像由PersistentContainer简称分贝实际上是文件在文件夹“StoreContent”下作为“persistentStore”

这可以解释为什么在我的情况下db“defaultDatabase”不能被PersistentContainer创建,如果你想要指定您的自定义数据库文件,或导致崩溃,因为文件夹已经存在。我进一步证实了这一附加一个文件名“MyDb.sqlite”是这样的:

let url = db.fileURL.appendingPathComponent("MyDb.sqlite") 
let storeDesription = NSPersistentStoreDescription(url: url) 
container.persistentStoreDescriptions = [storeDesription] 
print("store description \(container.persistentStoreDescriptions)" 
// store description [<NSPersistentStoreDescription: 0x60000005cc50> (type: SQLite, url: file:///Users/.../Documents/defaultDatabase/MyDb.sqlite) 
container.loadPersistentStores() { ... } 

这是新MyDb.sqlite:

enter image description here

基于上述分析,如果你有这样的代码:

if #available(iOS 10.0, *) { 
    // load db by using PersistentContainer 
    ... 
} else { 
    // Fallback on UIManagedDocument method to load db 
    ... 
} 

用户的设备可能在iOS 10.0之前,并且以后更新到10+。对于这个改变,我认为url必须被调整以避免崩溃或者创建一个新的(空的)db(丢失数据)。