0%

CoreData导入已经存在的sqlite文件

从零开始建 CoreData 数据的话,应该是常规操作了,但是如何导入已经存在的 sqlite 文件供 CoreData 使用呢?
昨天弄了半天,遇到第一个 not found entity 问题,用 momd文件解编译回xcode可识别文件 导入原始的 entity 结构解决。
完成后今天发现一个 mbp 可以正常读取数据了,但是回家 imac 上不能读取数据,依然出现 query entity 时候没有记录的情况。

基本上就是这两个主要的问题,谷歌了这样解决的,请接着往下看。
2021-03-25
已更新,请先看更新部分

  1. 步骤
    1. Step 1
    2. Step 2
    3. Step 3
    4. Step 4
    5. Step 5
  2. 更新
  3. 问题
    1. Application Support/xxx.sqlite: No such file or directory
    2. 找不到对应的 entity
    3. container.loadPersistentStores 成功后查找数据为0,但是有数据
  4. 参考

步骤

看看这个小哥的详细步骤。

Step 1

找到你要导入的 sqlite 文件,可能有三个文件 sqlite,sqlite-shm,sqlite-wal,导入一个 sqlite 也行的。

Populate your Core Data in another app and get files’ path using this code:

let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
print(documentsDirectory)

Step 2

导入文件

Drag your 3 files with .sqlite extension into your xCode project. (Be sure to select Add to targets option).

Step 3

添加这个方法,确保只设置一次。

Create function to check app’s first run in AppDelegate.swift

func isFirstLaunch() -> Bool {
let hasBeenLaunchedBeforeFlag = "hasBeenLaunchedBeforeFlag"
let isFirstLaunch = !UserDefaults.standard.bool(forKey: hasBeenLaunchedBeforeFlag)
if (isFirstLaunch) {
UserDefaults.standard.set(true, forKey: hasBeenLaunchedBeforeFlag)
UserDefaults.standard.synchronize()
}
return isFirstLaunch
}

Step 4

添加这个方法,这个地址是 sqlite 文件即将保存的位置,注意 .applicationSupportDirectory ??

Copy this function in AppDelegate.swift to get url where sqlite database should be moved:

func getDocumentsDirectory()-> URL {
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}

Step 5

执行替换

Replace declaration of persistentContainer with this one:

// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "ProjectName")

let storeUrl = self.getDocumentsDirectory().appendingPathComponent("FileName.sqlite")

if isFirstLaunch() {
let seededDataUrl = Bundle.main.url(forResource: "FileName", withExtension: "sqlite")
try! container.persistentStoreCoordinator.replacePersistentStore(at: storeUrl, destinationOptions: nil, withPersistentStoreFrom: seededDataUrl!, sourceOptions: nil, ofType: NSSQLiteStoreType)
}

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

注意 isFirstLaunch 这个判断句中的细节,之前我用的如下这个时好时坏??包括 FileManager 的 .applicationSupportDirectory 还是 .documentDirectory

if !FileManager.default.fileExists(atPath: (storeUrl.path)) {
let seededDataUrl = Bundle.main.url(forResource: appName, withExtension: "sqlite")
try! FileManager.default.copyItem(at: seededDataUrl!, to: storeUrl)
}

更新

最新教程按照这个来Preload Sqlite data to Core Data,其中把已有的 .sqlite copy 到相应路径时一定要注意获得路径的方法
就是不要自己拼接 Application Support 的路径 self.getDocumentsDirectory().appendingPathComponent("\(appName).sqlite")
而是用 NSPersistentContainer 的方法来获得 URL(fileURLWithPath: NSPersistentContainer.defaultDirectoryURL().relativePath + "/\(appName).sqlite")
自己拼接的路径会报错。
后一种方法可能会让 core data 去自动生成相关目录。

问题

主要问题

Application Support/xxx.sqlite: No such file or directory

把已有的 .sqlite copy 到相应路径时一定要注意获得路径的方法
就是不要自己拼接 Application Support 的路径 self.getDocumentsDirectory().appendingPathComponent("\(appName).sqlite")
而是用 NSPersistentContainer 的方法来获得 URL(fileURLWithPath: NSPersistentContainer.defaultDirectoryURL().relativePath + "/\(appName).sqlite")
自己拼接的路径会报错。

找不到对应的 entity

一般就是 xcdatamodeld 里的 entity 字段定义的有出入,大小写敏感的应该??

container.loadPersistentStores 成功后查找数据为0,但是有数据

搜索 pre-filled sqlite 找到 Any way to pre populate core data?,应该是没有正确设置 CoreData 中的 sqlite 文件导致的。

参考

Any way to pre populate core data?
Core Data: preloading an existing SQLite database
Preload Sqlite data to Core Data