Core data 如何为NSPersistentContainer设置自定义存储URL
如何将custom store.sqlite URL设置为NSPersistentContainer 我发现了一种丑陋的方法,将NSPersistentContainer子类化:Core data 如何为NSPersistentContainer设置自定义存储URL,core-data,swift3,Core Data,Swift3,如何将custom store.sqlite URL设置为NSPersistentContainer 我发现了一种丑陋的方法,将NSPersistentContainer子类化: final public class PersistentContainer: NSPersistentContainer { private static var customUrl: URL? public init(name: String, managedObjectModel model: NSManage
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)
然后,使用NSPersistentContainer
的persistentStoreDescriptions
属性告诉它使用此自定义位置
container.persistentStoreDescriptions = [description]
注意:
myURL
必须提供完整的/path/to/model.sqlite
,即使它还不存在。仅设置父目录不起作用。根据Tom的回答展开,当您出于任何目的使用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)")
}
}
我刚刚发现由
persistentcainer
创建的数据库的位置与由UIManagedDocument
创建的数据库不同。以下是通过UIManagedDocument
创建的数据库位置快照:
以下代码用于创建数据库:
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文件,则无法通过persistentcainer
创建db“defaultDatabase”,或者由于文件夹已经存在而导致崩溃。我通过添加文件名“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() { ... }
if #available(iOS 10.0, *) {
// load db by using PersistentContainer
...
} else {
// Fallback on UIManagedDocument method to load db
...
}
用户的设备可能在10.0之前的iOS上,然后更新到10+。对于此更改,我认为必须调整url以避免崩溃或创建新的(空的)db(丢失数据)。这是我用来初始化预填充的sqlite db的代码,该db工作一致。假设您将此数据库用作只读数据库,则无需将其复制到设备上的Documents目录
let repoName = "MyPrepopulatedDB"
let container = NSPersistentContainer(name: repoName)
let urlStr = Bundle.main.path(forResource: "MyPrepopulatedDB", ofType: "sqlite")
let url = URL(fileURLWithPath: urlStr!)
let persistentStoreDescription = NSPersistentStoreDescription(url: url)
persistentStoreDescription.setOption(NSString("true"), forKey: NSReadOnlyPersistentStoreOption)
container.persistentStoreDescriptions = [persistentStoreDescription]
container.loadPersistentStores(completionHandler: { description, error in
if let error = error {
os_log("ERROR: Failed to initialize persistent store, error is \(error.localizedDescription)")
} else {
os_log("Successfully loaded persistent store, \(description)")
}
})
需要记住的一些非常重要的步骤/项目:
- 构造sqlite文件的URL时,请使用初始值设定项的URL(fileURLWithPath:)形式。看起来核心数据需要基于文件的URL,否则会出现错误
- 我使用单元测试来运行一些代码,以便在模拟器中创建/预填充db
- 我通过在loadPersistentStores()的完成块中添加一条print语句,找到了sqlite文件的完整路径。此块的description参数包含sqlite文件的完整路径
- 然后使用Finder,您可以在应用程序项目中复制/粘贴该文件
- 与sqlite文件位于同一位置的还有另外两个文件(.sqlite shm和.sqlite wal)。将这两个文件也添加到项目中(与sqlite文件位于同一目录中)。如果没有它们,核心数据就会抛出错误
- 在persistentStoreDescription中设置nsreadOnlyPersistentStore选项(如上所示)。如果不这样做,您将得到一个警告(将来可能出现致命错误)