需要 iCloud 时如何处理首次启动体验?

Posted

技术标签:

【中文标题】需要 iCloud 时如何处理首次启动体验?【英文标题】:How to handle first time launch experience when iCloud is required? 【发布时间】:2020-04-20 05:32:58 【问题描述】:

我使用CloudKit 来存储公开可用的数据,并使用新的NSPersistentCloudKitContainer 作为我的核心数据堆栈的一部分来存储/同步私人数据。

当用户打开我的应用时,他们处于 4 种状态之一:

    他们是可以访问 iCloud 的新用户 他们是可以访问 iCloud 的老用户 他们是新用户,但由于某种原因无法访问 iCloud 他们是老用户,但由于某种原因无法访问 iCloud

状态 1 和 2 代表我的快乐之路。如果他们是新用户,我想在显示初始视图之前用一些数据播种用户的私人商店。如果他们是返回用户,我想从 Core Data 获取数据以传递给初始视图。

确定新老用户: 我的计划是使用NSUbiquitousKeyValueStore。我对此的关注是处理以下情况: 下载应用程序 -> 记录为之前启动过应用程序 -> 删除并在新设备上重新安装/安装应用程序 我假设NSUbiquitousKeyValueStore 需要一些时间来接收更新,所以我需要等到它完成同步后再进入初始视图。那么问题来了,如果他们无法访问 iCloud,会发生什么?如果收不到更新,NSUbiquitousKeyValueStore 如何告诉我他们是否是回访用户?

确定 iCloud 访问权限: 根据我所做的研究,我可以检查 FileManager.default.ubiquityIdentityToken 是否为 nil 以查看 iCloud 是否可用,但这不会告诉我为什么。我必须使用CKContainer.default().accountStatus 来了解为什么 iCloud 不可用。问题是这是一个异步调用,我的应用会在了解其帐户状态之前继续运行。

我真的在这个问题上摸不着头脑。优雅地确保处理所有这些状态的最佳方法是什么?

【问题讨论】:

您最终解决了这个问题吗?你是如何解决第 3 点和第 4 点的?我目前正在处理同样的问题,但还没有找到一个超级优雅的解决方案来解决用户未连接到 iCloud 的问题。 目前,我还没有找到完全满意的答案。正如一些人所建议的那样,没有一个单一的解决方案。目前,我将像用户可以访问 iCloud 一样处理我的应用程序。 【参考方案1】:

这里没有“正确”的答案,但我认为 NSUbiquitiousKeyValueStore 不会以任何方式获胜 - 就像你说的那样,如果他们没有登录 iCloud 或没有网络访问权限,它就不会为他们工作反正。我目前使用 NSUbiquitiousKeyValueStore 完成了一些与共享相关的工作,下次不会这样做。我真的希望 NSPersistentCloudKitContainer 支持 ios 14 中的共享,并且我可以一举清除大部分 CloudKit 代码。

如果您的应用在没有云访问的情况下无法正常运行,那么您可能只需在屏幕上显示这一点,尽管总的来说这并不是非常令人满意的用户体验。我这样做的方式是将 iCloud 同步视为真正的异步(确实如此)。所以我允许用户开始使用该应用程序。然后您可以调用 accountStatus 以查看它是否在后台可用。如果是,则开始同步,如果不是,则等到它是,然后开始该过程。

因此,用户可以在设备上无限期地独立使用该应用程序,并且在他们连接到互联网时,他们在任何其他设备上所做的一切都会合并到他们在这台新设备上所做的事情中。

【讨论】:

感谢您的回复。我想这可以归结为NSUbiquitousKeyValueStore 承诺的“最终一致性”。据我了解,即使用户无法访问 iCloud,他们的偏好仍将存储在磁盘上等待同步。您能否分享更多关于您在使用NSUbiquitousKeyValueStore 时遇到的问题的详细信息?【参考方案2】:

我最近也遇到了这个问题。我想出的解决方案是直接用CloudKit查询iCloud,看看是否已经初始化。其实很简单:

public func checkRemoteData(completion: @escaping (Bool) -> ()) 
    let db = CKContainer.default().privateCloudDatabase
    let predicate = NSPredicate(format: "CD_entityName = 'Root'")
    let query = CKQuery(recordType: .init("CD_Container"), predicate: predicate)
    db.perform(query, inZoneWith: nil)  result, error in
        if error == nil 
            if let records = result, !records.isEmpty 
                completion(true)
             else 
                completion(false)
            
         else 
            print(error as Any)
            completion(false)
        
    

此代码说明了一个更复杂的情况,其中您有一个带有派生模型的Container 实体实例,在本例中称为Root。我有类似的东西,并且可以使用根的存在作为数据已设置的证据。

有关如何将核心数据信息传输到 iCloud 的第一手文档,请参见此处:https://developer.apple.com/documentation/coredata/mirroring_a_core_data_store_with_cloudkit/reading_cloudkit_records_for_core_data

【讨论】:

这是一个可行的建议,帮助我解决了类似的问题,谢谢

以上是关于需要 iCloud 时如何处理首次启动体验?的主要内容,如果未能解决你的问题,请参考以下文章

db关闭时如何处理flyway

android服务重新启动时如何处理远程对象?

IOS,应用启动时如何处理多个本地通知?

使用 Envers 时如何处理 Flyway 迁移?

iCloud 和 CoreData:更改为 iCloud Store 时的通知(使用现有 iCloud 数据首次启动)

当应用程序不在后台运行时如何处理推送通知