【问题标题】:Core Data+CloudKit not connecting to CloudKitCore Data+CloudKit 未连接到 CloudKit
【发布时间】:2020-11-02 04:02:50
【问题描述】:

我正在更新 iOS13 之前的 Core Data 应用程序以使用 Core Data+CloudKit 同步来支持多个设备上的单个用户。同步应该自动发生,并且在我开发的一个临时步骤中它确实有效。现在它不工作了,CloudKit 遥测没有注册任何活动,我不知道为什么它不工作。

在我以前的应用程序版本中,我在 UserDefaults 中提供了少量标签字符串并允许用户修改它们,更新的版本放回 UserDefaults。这是避免必须为少数对象管理第二个核心数据实体的捷径。

我已经意识到这在多设备实现中不起作用,因为本地 Core Data 数据库为空的事实不再意味着它是该用户的第一次使用。相反,每个新设备都需要查看基于云的数据源以查找用户正在使用的标签字符串,并且仅在用户没有其他内容时才使用应用提供的默认值。

我按照 Apple 对 Setting Up Core Data with CloudKit 的说明进行操作,起初同步工作正常。但后来我意识到同步行为是不正确的,而不是从存储在 UserDefaults 中的字符串预填充,我真的需要提供一个预填充的核心数据数据库(.sqlite 文件)。我实现了这一点,并且在首次本地启动时复制捆绑的 .sqlite 文件后,该应用现在在本地运行良好。

但由于某种原因,此更改导致 CloudKit 同步停止工作。现在,我不仅没有在其他设备上获得任何自动更新,而且在 CloudKit 仪表板遥测中也没有得到任何结果,因此 CloudKit 同步似乎从未开始。这很奇怪,因为我在本地收到刚刚在本地发生的“远程”更改的通知(当我在本地保存新数据时,我列为通知的#selector 的函数在本地被调用)。

我不知道问题/解决方案是什么。这是我的相关代码。

//In my CoreDataHelper class
lazy var context = persistentContainer.viewContext

lazy var persistentContainer: NSPersistentCloudKitContainer = {

    let appName = Bundle.main.infoDictionary!["CFBundleName"] as! String
    let container = NSPersistentCloudKitContainer(name: appName)
    
    //Pre-load my default Core Data data (Category names) on first launch
    let storeUrl = FileManager.default.urls(for: .applicationSupportDirectory, in:.userDomainMask).first!.appendingPathComponent(appName + ".sqlite")
    let storeUrlFolder = FileManager.default.urls(for: .applicationSupportDirectory, 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"))
    }

    let storeDescription = NSPersistentStoreDescription(url: storeUrl)
    storeDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)

    //In the view controllers, we'll listen for relevant remote changes
    let remoteChangeKey = "NSPersistentStoreRemoteChangeNotificationOptionKey"
    storeDescription.setOption(true as NSNumber, forKey: remoteChangeKey)

    container.persistentStoreDescriptions = [storeDescription]
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    
    //This is returning nil but I don't think it should
    print(storeDescription.cloudKitContainerOptions?.containerIdentifier)
    
    return container
}()

//In my view controller

let context = CoreDataHelper.shared.context

override func viewDidLoad() {
    super.viewDidLoad()
    
    //Do other setup stuff, removed for clarity
    
    //enable CloudKit syncing
    context.automaticallyMergesChangesFromParent = true
}

override func viewWillAppear(_ animated: Bool) {
    clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
    super.viewWillAppear(animated)
    
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(reportCKchange),
        name: NSNotification.Name(
            rawValue: "NSPersistentStoreRemoteChangeNotification"),
        object: CoreDataHelper.shared.persistentContainer.persistentStoreCoordinator
    )
    updateUI()
}

@objc
fileprivate func reportCKchange() {
    print("Change reported from CK")
    tableView.reloadData()
}

注意:我已将目标更新为 iOS13+。

【问题讨论】:

    标签: ios swift core-data cloudkit nspersistentcloudkitcontainer


    【解决方案1】:

    我认为新创建的NSPersistentStoreDescription 默认没有cloudKitContainerOptions

    要设置它们,请尝试:

    storeDescription.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: <<Your CloudKit ID>>)

    【讨论】:

    • Ch Ryder 再次出手相救。谢谢。
    • 现在 CloudKit 正在同步,我从 Xcode 控制台中的同步过程中获得了数十行状态更新。你知道是否有可能减少这个的冗长?
    • 我不建议隐藏这些日志,因为它们包含有用且可能相关的信息,但如果您真的想要,请在此处解决:stackoverflow.com/questions/57905451/…
    【解决方案2】:

    我还发现您的实体的所有属性都必须是可选的,或者具有默认值,否则它将无法正常工作。如果这在文档中更清楚,那就太好了...

    【讨论】:

    • 是的,它说不支持可选relationships,但我也没有意识到所有实体attributes 也必须是可选的(或具有默认值)——但至少为此,控制台中的错误消息是描述性的,希望是完整的......
    猜你喜欢
    • 2021-07-19
    • 2020-02-29
    • 1970-01-01
    • 2020-03-14
    • 1970-01-01
    • 2022-07-03
    • 2014-08-06
    • 2021-01-06
    • 2020-03-26
    相关资源
    最近更新 更多