预填充 .sqlite 的核心数据 (Swift3)
Core Data with pre-filled .sqlite (Swift3)
目前我正在对现有 iOS9 应用程序进行 Swift3 / iOS10 更新,该应用程序存储了整个欧洲大约 10.000 个电动汽车充电点。到目前为止,我总是使用预填充的数据库(.xcappdata 包中的 .sqlite、.sqlite-shm、.sqlite-wal 文件)运送应用程序,但在当前版本中,Apple 引入了 NSPersistentContainer Class,这让它变得有点复杂。在我的 AppDelegate Class 中,我正在实例化我的 NSPersistentContainer 对象并将其传递给惰性变量,就像 Apple 在每个示例代码中所做的那样:
lazy var stationDataPersistentContainer: NSPersistentContainer = {
let fileMgr = FileManager.default
let destinationModel = NSPersistentContainer.defaultDirectoryURL()
if !fileMgr.fileExists(atPath: destinationModel.appendingPathComponent("StationData.sqlite").path) {
do {
try fileMgr.copyItem(at: URL(fileURLWithPath: Bundle.main.resourcePath!.appending("/StationData.sqlite")), to: destinationModel.appendingPathComponent("/StationData.sqlite"))
try fileMgr.copyItem(at: URL(fileURLWithPath: Bundle.main.resourcePath!.appending("/StationData.sqlite-shm")), to: destinationModel.appendingPathComponent("/StationData.sqlite-shm"))
try fileMgr.copyItem(at: URL(fileURLWithPath: Bundle.main.resourcePath!.appending("/StationData.sqlite-wal")), to: destinationModel.appendingPathComponent("/StationData.sqlite-wal"))
} catch {
//
}
} else {
//
}
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "StationData")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
/*
* Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
* Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
在 iOS9 版本中,我将文件复制到适当的目录,如您在以下代码示例中所见:
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("StationData.sqlite")
let fileMgr = NSFileManager.defaultManager()
if !fileMgr.fileExistsAtPath(url.path!) {
do {
try fileMgr.copyItemAtPath(NSBundle.mainBundle().pathForResource("StationData", ofType: "sqlite")!, toPath: self.applicationDocumentsDirectory.URLByAppment("StationData.sqlite").path!)
try fileMgr.copyItemAtPath(NSBundle.mainBundle().pathForResource("StationData", ofType: "sqlite-shm")!, toPath: self.applicationDocumentsDirectory.URLByAppendingPathComponent("StationData.sqlite-shm").path!)
try fileMgr.copyItemAtPath(NSBundle.mainBundle().pathForResource("StationData", ofType: "sqlite-wal")!, toPath: self.applicationDocumentsDirectory.URLByAppendingPathComponent("StationData.sqlite-wal").path!)
} catch {
//
} do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url,
options: [NSMigratePersistentStoresAutomaticallyOption:true, NSInferMappingModelAutomaticallyOption:true])
} catch {
//
}
} else {
//
}
return coordinator
}()
几天来,我一直试图将文件移动到 NSPersistentContainer.defaultDirectoryURL() -> URL
返回的正确目录,但每次出现错误时,都会提示该文件已经存在,因为我的 stationDataPersistentContainer 已经初始化并且所以 NSPersistentContainer 有足够的时间来生成 sqlite* 文件。即使我尝试复制文件并在覆盖的 init() 函数中初始化 stationDataPersistentContainer,我也无法做到这一点。文档中有什么我遗漏或忽略的吗? best/right/appropriate 将安装应用程序的现有数据复制到 coredata 的方法。
附录:
仅供参考,我还可以将从我的 API 中获取的 JSON- 文件存储到 Documents 目录中,并 运行 JSON-解析器,但这需要大量资源,尤其是时间! (这个问题也发在苹果开发者论坛上,等待审核)
最简单的解决方案是不使用 NSPersistentContainer - 它只是为了方便删除堆栈样板,如果您正在更新已经运行的应用程序,您可以保持代码不变。
如果您想迁移到 NSPersistentContainer,请在调用 loadPersistentStores
之前移动文件 - 正是在那个时候首先创建了 SQL 文件。
请注意,NSPersistentContainer 可能未使用文档目录 - iOS 模拟器开箱即用,它使用应用程序支持目录。如果使用文档目录对您很重要,那么您必须子 class NSPersistentContainer 并覆盖 defaultDirectoryURL class 方法,或者向 NSPersistentContainer 提供一个 NSPersistentStoreDescription 告诉它在哪里存储数据。
我是这样做的:
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "app_name")
let seededData: String = "app_name"
var persistentStoreDescriptions: NSPersistentStoreDescription
let storeUrl = self.applicationDocumentsDirectory.appendingPathComponent("app_name.sqlite")
if !FileManager.default.fileExists(atPath: (storeUrl.path)) {
let seededDataUrl = Bundle.main.url(forResource: seededData, withExtension: "sqlite")
try! FileManager.default.copyItem(at: seededDataUrl!, to: storeUrl)
}
print(storeUrl)
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeUrl)]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error {
fatalError("Unresolved error \(error),")
}
})
return container
}()
目前我正在对现有 iOS9 应用程序进行 Swift3 / iOS10 更新,该应用程序存储了整个欧洲大约 10.000 个电动汽车充电点。到目前为止,我总是使用预填充的数据库(.xcappdata 包中的 .sqlite、.sqlite-shm、.sqlite-wal 文件)运送应用程序,但在当前版本中,Apple 引入了 NSPersistentContainer Class,这让它变得有点复杂。在我的 AppDelegate Class 中,我正在实例化我的 NSPersistentContainer 对象并将其传递给惰性变量,就像 Apple 在每个示例代码中所做的那样:
lazy var stationDataPersistentContainer: NSPersistentContainer = {
let fileMgr = FileManager.default
let destinationModel = NSPersistentContainer.defaultDirectoryURL()
if !fileMgr.fileExists(atPath: destinationModel.appendingPathComponent("StationData.sqlite").path) {
do {
try fileMgr.copyItem(at: URL(fileURLWithPath: Bundle.main.resourcePath!.appending("/StationData.sqlite")), to: destinationModel.appendingPathComponent("/StationData.sqlite"))
try fileMgr.copyItem(at: URL(fileURLWithPath: Bundle.main.resourcePath!.appending("/StationData.sqlite-shm")), to: destinationModel.appendingPathComponent("/StationData.sqlite-shm"))
try fileMgr.copyItem(at: URL(fileURLWithPath: Bundle.main.resourcePath!.appending("/StationData.sqlite-wal")), to: destinationModel.appendingPathComponent("/StationData.sqlite-wal"))
} catch {
//
}
} else {
//
}
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "StationData")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
/*
* Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
* Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
在 iOS9 版本中,我将文件复制到适当的目录,如您在以下代码示例中所见:
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("StationData.sqlite")
let fileMgr = NSFileManager.defaultManager()
if !fileMgr.fileExistsAtPath(url.path!) {
do {
try fileMgr.copyItemAtPath(NSBundle.mainBundle().pathForResource("StationData", ofType: "sqlite")!, toPath: self.applicationDocumentsDirectory.URLByAppment("StationData.sqlite").path!)
try fileMgr.copyItemAtPath(NSBundle.mainBundle().pathForResource("StationData", ofType: "sqlite-shm")!, toPath: self.applicationDocumentsDirectory.URLByAppendingPathComponent("StationData.sqlite-shm").path!)
try fileMgr.copyItemAtPath(NSBundle.mainBundle().pathForResource("StationData", ofType: "sqlite-wal")!, toPath: self.applicationDocumentsDirectory.URLByAppendingPathComponent("StationData.sqlite-wal").path!)
} catch {
//
} do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url,
options: [NSMigratePersistentStoresAutomaticallyOption:true, NSInferMappingModelAutomaticallyOption:true])
} catch {
//
}
} else {
//
}
return coordinator
}()
几天来,我一直试图将文件移动到 NSPersistentContainer.defaultDirectoryURL() -> URL
返回的正确目录,但每次出现错误时,都会提示该文件已经存在,因为我的 stationDataPersistentContainer 已经初始化并且所以 NSPersistentContainer 有足够的时间来生成 sqlite* 文件。即使我尝试复制文件并在覆盖的 init() 函数中初始化 stationDataPersistentContainer,我也无法做到这一点。文档中有什么我遗漏或忽略的吗? best/right/appropriate 将安装应用程序的现有数据复制到 coredata 的方法。
附录:
仅供参考,我还可以将从我的 API 中获取的 JSON- 文件存储到 Documents 目录中,并 运行 JSON-解析器,但这需要大量资源,尤其是时间! (这个问题也发在苹果开发者论坛上,等待审核)
最简单的解决方案是不使用 NSPersistentContainer - 它只是为了方便删除堆栈样板,如果您正在更新已经运行的应用程序,您可以保持代码不变。
如果您想迁移到 NSPersistentContainer,请在调用 loadPersistentStores
之前移动文件 - 正是在那个时候首先创建了 SQL 文件。
请注意,NSPersistentContainer 可能未使用文档目录 - iOS 模拟器开箱即用,它使用应用程序支持目录。如果使用文档目录对您很重要,那么您必须子 class NSPersistentContainer 并覆盖 defaultDirectoryURL class 方法,或者向 NSPersistentContainer 提供一个 NSPersistentStoreDescription 告诉它在哪里存储数据。
我是这样做的:
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "app_name")
let seededData: String = "app_name"
var persistentStoreDescriptions: NSPersistentStoreDescription
let storeUrl = self.applicationDocumentsDirectory.appendingPathComponent("app_name.sqlite")
if !FileManager.default.fileExists(atPath: (storeUrl.path)) {
let seededDataUrl = Bundle.main.url(forResource: seededData, withExtension: "sqlite")
try! FileManager.default.copyItem(at: seededDataUrl!, to: storeUrl)
}
print(storeUrl)
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeUrl)]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error {
fatalError("Unresolved error \(error),")
}
})
return container
}()