【问题标题】:CloudKit Sync using NSPersistentCloudKitContainer in iOS13在 iOS13 中使用 NSPersistentCloudKitContainer 进行 CloudKit 同步
【发布时间】:2020-01-30 10:55:04
【问题描述】:

我正在为我的核心数据应用程序使用 NSPersistentCloudKitContainer。在测试期间,我检查了在设备上所做的更改是否会在几秒钟内同步到 CloudKit。

但是,当我在设备上禁用 iCloud 然后立即重新启用时,设备上的所有数据都消失了。我检查了我的私有数据库中的数据是否仍然存在于 CloudKit 上。 CloudKit 上的数据同步回我的设备需要 1 天多的时间。

当用户更换设备并看到他们的数据最初消失时,这会给用户带来困惑。问题:如何控制 CloudKit 上的数据同步回我的设备的速度?

【问题讨论】:

    标签: core-data cloudkit ios13


    【解决方案1】:

    令人沮丧的是,我认为这是“正常”行为,尤其是对于具有大量要同步的关系的大型数据库 - 无法查看进度(向用户显示),也无法加快进度。

    NSPersistentCloudKitContainer 似乎将每个关系视为单独的CKRecord,同步仍然受到相同限制(即一次不超过 400 个“请求”),您会经常看到这些 .limitExceeded 错误在控制台中,但几乎没有其他信息(即是否/何时重试??)。

    我发现这导致数据库需要 天才能完全同步,同时数据看起来混乱且不完整。我有 数千个 多对多关系,当用户从外部文件恢复他们的数据库时,所有这些 CKRecords 都需要重新创建。

    我在这里主要担心的是无法查询NSPersistentCloudKitContainer 是否有待处理的请求,还有多少尚未同步等,因此您可以将其转发给 UI 中的用户,以便他们不会一直认为它“失败”而删除和恢复。

    当你关闭同步时本地数据被删除的一种方法 - 并且可能在你重新打开它时不必让它全部“重新同步” - 是在它关闭时使用NSPersistentContainer,然后使用NSPersistentCloudKitContainer 开启时。

    NSPersistentCloudKitContainerNSPersistentContainer 的子类。

    我目前正在我的自定义 PersistenceService 类中的应用程序中执行此操作:

    static var useCloudSync = UserDefaults.standard.bool(forKey: "useCloudSync")
    static var persistentContainer:  NSPersistentContainer  = {
        let container: NSPersistentContainer?
        if useCloudSync {
            container = NSPersistentCloudKitContainer(name: "MyApp")
        } else {
            container = NSPersistentContainer(name: "MyApp")
            let description = container!.persistentStoreDescriptions.first
            description?.setOption(true as NSNumber,
                                   forKey: NSPersistentHistoryTrackingKey)
    
        }
        container!.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
        })
        return container!
    }()
    

    这至少会导致在您的应用中关闭 iCloud 时本地数据不会受到影响,并且在重新打开时不需要重新同步所有内容。

    我想你也可以在NSPersistentCloudKitContainer删除所有本地数据之前查询iOS,看看用户是否在系统设置中关闭了iCloud,并在两者之间切换。

    编辑:添加了NSPersistentHistoryTrackingKey,因为没有它,从NSPersistentCloudKitContainer 切换回NSPersistentContainer 失败。

    它在我的应用程序中正常工作。当用户在我的应用程序中重新启用 iCloud 同步(并从 NSPersistentContainer 切换到 NSPersistentCloudKitContainer )时,它会同步自上次同步以来添加/更改的任何内容,这正是我想要的!

    编辑 2:这是上述方法的更好实现

    基本上,无论用户是否同步到 iCloud,只需将容器上的 .options 更改为使用 NSPersistentCloudKitContainerOptions(containerIdentifier:)nil。似乎根本不需要在 NSPersistentCloudKitContainerNSPersistentContainer 之间切换。

    static var synciCloudData = {
        return defaults.bool(forKey: Settings.synciCloudData.key)
    }()
    
    static var persistentContainer:  NSPersistentContainer  = {
        let container = NSPersistentCloudKitContainer(name: "AppName")
        
        guard let description = container.persistentStoreDescriptions.first else {
            fatalError("Could not retrieve a persistent store description.")
        }
        
        description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        
        if synciCloudData {
            let cloudKitContainerIdentifier = "iCloud.com.yourID.AppName"
            let options = NSPersistentCloudKitContainerOptions(containerIdentifier: cloudKitContainerIdentifier)
            
            description.cloudKitContainerOptions = options
        } else {
            description.cloudKitContainerOptions = nil
        }
                
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        
        return container
    }()
    

    最后,您可以看到 iCloud 同步的状态,尽管很粗略(即,您无法看到是否有任何东西“同步”,只有同步处于挂起/正在进行以及是否它成功或失败。话虽如此,这足以满足我在我的应用程序中的用例。

    查看用户 ggruen 在帖子底部的回复: NSPersistentCloudKitContainer: How to check if data is synced to CloudKit

    【讨论】:

    • 正如你所说,它工作正常,但一旦重新启动应用程序就会开始工作。
    • 不需要重启应用,我已经测试过具有相同模型对象的 NSPersistentContainer 对象可以在不重启的情况下进行交换。
    • 非常有帮助,请您解释一下如何在不重启应用的情况下在 NSPersistentContainer 和 NSPersistentCloudKitContainer 之间切换。我正在尝试一些事情,但没有成功。提前致谢。
    • 另请注意,如果您将NSPersistentStoreDescription 设置为通过description.cloudKitContainerOptions?.databaseScope = .public 与公共数据库而不是私有数据库(我相信iOS 14 之前的默认值)交互,它只会轮询更改设计为每 30 分钟一次(请参阅 WWDC 2020 会议视频中关于此主题的 10:30:developer.apple.com/videos/play/wwdc2020/10650
    • @garanda 你知道如何在不重启应用的情况下将NSPersistentHistoryTrackingKey 添加到你的cloudkit 吗?我现在也有类似的问题
    猜你喜欢
    • 2020-03-27
    • 1970-01-01
    • 1970-01-01
    • 2021-02-07
    • 2020-11-07
    • 2021-11-30
    • 1970-01-01
    • 2016-01-27
    • 1970-01-01
    相关资源
    最近更新 更多