【问题标题】:How to pre-load Core Data SQLite files with NSPersistentCloudKitContainer如何使用 NSPersistentCloudKitContainer 预加载 Core Data SQLite 文件
【发布时间】:2020-10-23 15:53:45
【问题描述】:

我有一个 Core Data+CloudKit 配置,我需要为用户提供一些初始数据作为起点。它只有几个记录/对象,所以起初我尝试对数据进行硬编码并在首次启动时写入 Core Data。然后,用户可以添加和更改起点数据。

当我启用 CloudKit 同步时,我发现当启动同一帐户上的第二台设备时,从 CloudKit 的角度来看,该设备新写入的默认数据是“更新的”。这意味着第二台设备上的默认数据将同步并覆盖用户自己在第一台设备上的数据。这很糟糕。

我相信正确的解决方案是在应用程序中提供预填充的核心数据文件 (.sqlite) 文件,以便在首次启动时,默认数据已经比用户在其他设备上创建的任何数据都旧。大多数教程和示例代码都非常旧,参考 Swift 3、Xcode 7 等。我正在寻找一个在使用 NSPersistentCloudKitContainer 时有效的解决方案。

我尝试了this SO question 答案中的代码,将persistentContainer 的类型更改为NSPersistentCloudKitContainer。这会编译并运行,但结果是我的应用程序在没有预加载数据的情况下启动,我不知道为什么。我在我的 Xcode 项目中包含了三个 .sqlite 文件,并将它们添加到“构建阶段”中的“复制捆绑资源”列表中。

这是我的代码:

lazy var persistentContainer: NSPersistentCloudKitContainer = {

    let appName = Bundle.main.infoDictionary!["CFBundleName"] as! String
    let container = NSPersistentCloudKitContainer(name: appName)
    
    //Pre-load the default Core Data (Category names) on first launch
    var persistentStoreDescriptions: NSPersistentStoreDescription

    let storeUrl = FileManager.default.urls(for: .documentDirectory, in:.userDomainMask).first!.appendingPathComponent(appName + ".sqlite")
    let storeUrlFolder = FileManager.default.urls(for: .documentDirectory, in:.userDomainMask).first!

    if !FileManager.default.fileExists(atPath: (storeUrl.path)) {
        let seededDataUrl = Bundle.main.url(forResource: appName, withExtension: "sqlite")
        let seededDataUrl2 = Bundle.main.url(forResource: appName, withExtension: "sqlite-shm")
        let seededDataUrl3 = Bundle.main.url(forResource: appName, withExtension: "sqlite-wal")

        try! FileManager.default.copyItem(at: seededDataUrl!, to: storeUrl)
        try! FileManager.default.copyItem(at: seededDataUrl2!, to: storeUrlFolder.appendingPathComponent(appName + ".sqlite-shm"))
        try! FileManager.default.copyItem(at: seededDataUrl3!, to: storeUrlFolder.appendingPathComponent(appName + ".sqlite-wal"))
    }
    
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
         
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

【问题讨论】:

  • 我已经取得了一些进展,并了解到在从应用程序实例保存它们然后在 Xcode 中安装的过程中,.sqlite 文件很容易损坏。发生这种情况时,应用程序无法在启动时读取数据,但也不会报告错误。一旦我得到未损坏的文件,它就开始正常工作。所以现在我可以成功安装默认数据,但这已经发现了其他问题。当我弄清楚发生了什么,或者当我能提出一个合理的问题时,我会更新这个页面。
  • 我发现 sqlite 文件不太可能容易损坏。保存数据,关闭应用程序并将其复制到另一个文件夹就可以了。

标签: ios swift core-data cloudkit nspersistentcloudkitcontainer


【解决方案1】:

我相信您没有看到任何数据,因为您的代码会查找 .documentDirectory,而 PersistentCloudKitContainer 默认会在 Application Support 目录中查找数据库。

【讨论】:

  • 这可能是问题的一部分。我将 .documentDirectory 更改为 .applicationSupportDirectory 但应用​​程序仍未获取数据。我还确认 .sqlite 文件没有损坏。我仍然不清楚为什么 Core Data 似乎没有连接到这些文件。
  • 只是盯着你的代码,你确定你初始化NSPersistentCloudKitContainer 正确吗?我本来希望init 带有您的数据库模型的名称,而不是应用程序的名称。除此之外,我不确定自己还能做出更多贡献。
  • 对于 Core Data,数据库的名称与应用程序的名称相同,至少默认情况下是这样。我不确定它是否可以更改。
  • 我觉得应该是xcdatamodeld文件的名字。这不一定是应用名称。
猜你喜欢
  • 2013-10-08
  • 1970-01-01
  • 2021-10-17
  • 2012-04-25
  • 2011-09-28
  • 2014-09-20
  • 2011-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多