Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jsp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Core data 如何为NSPersistentContainer设置自定义存储URL_Core Data_Swift3 - Fatal编程技术网

Core data 如何为NSPersistentContainer设置自定义存储URL

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

如何将custom store.sqlite URL设置为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)
然后,使用
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选项(如上所示)。如果不这样做,您将得到一个警告(将来可能出现致命错误)

这似乎只在store.sqlite已经存在的情况下有效,而不是在初始化CoreData堆栈时。不,如果文件不存在,它可以正常工作。但是您希望文件进入的目录必须存在,并且您的应用程序组需要正确设置。不过,不要求存储文件已经存在。奇怪。它在此处崩溃,出现“未找到文件”错误。但是,如果文件已经存在,则可以正常工作。这就是它应该如何工作的。如果它对你不起作用,那么你还有另一个问题。比如目录可能不存在。