将 Core Data 数据文件备份成 sqlite 文件

额我知道 Core Data 默认就是以 sqlite 文件保存的数据…题目上说的意思是将这个数据文件备份出来给其他的 Core Data 栈使用, 比如我的应用场景是把手机上 Core Data 文件同步到手表 app 上(当然数据量很小才这么干).


目录

直接用原来的 sqlite 文件不行么

不行, 亲测!

如果进到 Core Data 保存的目录下可以看到, 除了 sqlite 文件之外, 还会有预写式日志文件(.whl)和共享内存文件(.shm), 当前的数据可能还在读写, 或许有些还没有写入 .sqlite 文件, 所以仅仅复制 .sqlite 文件是不可取的, 并且 .whl 和 .shm 文件也不好处理. 最好是可以找到 Core Data 自身的相关 API.

用得到的 Core Data API

看向 Core Data 栈的底层, 可以发现NSPersistentCoordinator类下有一个migratePersistentStore(_:to:options:withType:)方法, 该方法会把传入的NSPersistentStore文件复制到所传入的新 URL 的位置, 正是我们想要的 API.

不过这个 API 本来是用于转移数据位置的, 会导致原来的NSPersistentCoordinator移掉这个NSPersistentStore. 我们想要的功能仅仅是把数据复制一下, 而原来的 Core Data 继续生效, 所以还需要再创建一个临时的NSPersistentCoordinator指向这个NSPersistentStore.

也就是说, 我们要在一个NSPersistentStore上建立两个NSPersistentCoordinator, 为了不对原来的 Core Data 产生影响, 并且也没有干预原来数据的必要, 我们要设置这个临时的NSPersistentcoordinatorNSPersistentStore为只读. 同时像上面说的, .whl 和 .shm 文件不好处理, 所以对于转移后的新文件, 我们要关闭日志功能.

问题解决

上代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func backupFile() -> URL {
    // 以只有一个文件为例
    let sourceStore = persistentStores.first!
    // 使用原来的数据模型创建一个临时 NSPersistentCoordinator
    let backupCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
    // 设置下面要添加 NSPersistentStore 的配置, 需要保存证为只读
    let options = (sourceStore.options ?? [:]).merging([NSReadOnlyPersistentStoreOption: true]) { $1 }
    // 将原来的 NSPerstentStore 添加进来, 除了配置之外, 其他参数都使用原来的即可
    let backupStore = try! backupCoordinator.addPersistentStore(ofType: sourceStore.type,
                                                                configurationName: sourceStore.configurationName,
                                                                at: sourceStore.url,
                                                                options: options)
    // 设置下面转移文件的配置, 还是设置为只读, 而且关闭日志, 在转移的时候整理原来的数据碎片
    let backupStoreOptions: [AnyHashable: Any] = [NSReadOnlyPersistentStoreOption: true,
                                                  NSSQLitePragmasOption: ["journal_mode": "DELETE"],
                                                  NSSQLiteManualVacuumOption: true]
    // 提供数据要转换到的 URL
    let url = generateUniqueURL()
    // 转移数据, 并且指定一下保存为 sqlite 文件
    try! backupCoordinator.migratePersistentStore(backupStore,
                                                  to: url,
                                                  options: backupStoreOptions,
                                                  withType: NSSQLiteStoreType)
    return url
}

最后别放了使用之后处理一下生成和备份文件.


emmmm, 上次更新服务器系统时忘了备份数据库, 再加上评论比较少, 暂时关闭评论功能