您在问题中描述的架构是使用 Core Data 和 CloudKit 运行良好的架构。可能还有其他值得探索的选项(例如 Firebase、Realm),但由于您提到了 Apple 框架,我将回答这些问题。这都是我自己的经验,所以你的里程可能会有所不同。
让我们从简化的答案开始。
Apple 提供NSPersistentCloudKitContainer,它封装了核心数据堆栈并将持久存储镜像到 CloudKit 私有数据库。尽管这对于新应用程序来说是一个很好的解决方案,但它不能与现有的 CloudKit 容器一起使用。 “Core Data 拥有从 Core Data 模型创建的 CloudKit 方案。现有的 CloudKit 容器与此模式不兼容。”但它仍然可能是一条值得探索的道路,并且有大量可用资源:
现在,让我们深入了解一些细节。
在NSPersistentCloudKitContainer 之前,应用需要管理Core Data 和CloudKit 框架之间的同步和存储。尽管这可能看起来像是一个女儿的任务,但它是一个可以管理的任务。在接近解决方案时需要牢记以下几点:
-
最好将您的 CloudKit 存储视为事实来源。
-
维护 Core Data 记录和 CloudKit 记录之间的唯一标识符是必不可少的。
-
您的应用/用户体验主要应该来自 Core Data 存储。
-
处理任务——比如创建配方、持久化配方、更新配方——因为Operations 可能是有益的。
CloudKit
公共/私人/共享访问是 CloudKit 运作方式的核心。一个 CloudKit 容器具有三个数据库 (CKDatabase):
-
privateCloudDatabase:所有者可读,所有者可写。您无法通过 Developer Portal 看到。
-
publicCloudDatabase:世界可读,所有者可写。可以被角色锁定,并且可以通过开发者门户对您可见。
-
sharedCloudDatabase:共享参与者 (CKShare) 可用,您不可见。
这种结构非常符合您希望将公共数据和私有/用户数据分开的愿望。
可以在数据库上设置订阅 (CKSubscription) 以通知您已发生更改。此时,您可以让您的应用程序获取更改的记录。您可以提供一个更改令牌 (CKServerChangeToken),以将查询结果限制为仅显示自上次下载以来发生更改的记录。每个设备都有自己的订阅和令牌,因此它只会下载所需的数据并记录更改。通过这种方式,CloudKit 是事实的来源,所有的变化——不管它们发生在哪里——都会反映在其他设备上。
您不必等待通知发生即可查询更改。 CKFetchRecordZoneChangesOperation 将始终在执行竞争时提供新的更改令牌。因此,无论如何触发,检索更改都是相同的过程;通过通知或其他机制(如连接更改)或手动进行。
您可能希望为“公共”和“私有”数据库创建订阅,以确保可以监控两者的更改。
CloudKit 和核心数据架构
Core Data 和 CloudKit 之间的模型结构有一个重要区别,这以关系的形式出现。
Core Data 建议所有关系都是双向的。例如:Recipe 将与其Ingredients 有关系(一对多),而Ingredient 将与其Recipe 有关系(一对一)。
在 CloudKit 中,仅建议使用单向关系,使用 CKReference。因此,对于给出的示例,Recipe 不会直接引用成分,但 Ingredient 将具有对使用它的 Recipe 的关系引用。
作为 Core Data 和 CloudKit 之间互操作的一部分,您需要一个在两个环境之间唯一且可查询的标识符。实现此目的的常用方法是使用UUID。 CKRecord.ID 由一个“recordName”(字符串)和一个“zoneID”(CKRecordZone.ID)组成。提供 UUID.uuidString 作为“recordName”将在 CloudKit 中创建唯一记录,但也会为您提供对 Core Data 中实体的一致引用。
核心数据
正如您所指出的,离线访问数据对应用的可用性至关重要。 Core Data 在这里是一个不错的选择。即使您可以直接从CKQuery 显示结果,但保留该数据确实有一些优势。我将本地核心数据存储视为我的应用程序中所有数据的接口。这意味着我的用户数据只有一个接口。
对本地数据所做的任何更改也会发送到 CloudKit,来自 CloudKit 的任何记录都会在本地核心数据存储中添加或修改。
以这种方式使用 Core Data 可以让您利用 NSFetchedResultsController 等工具,这些工具可用于自动反映对一组实体所做的更改。
此外,您还可以连接到 Notification NSManagedObjectContextDidSave,在从 CloudKit 交互合并更改时帮助您的应用 UI 保持最新。
请记住,尝试使用 NSPersistentContainer.viewContext 执行大量任务可能会导致性能问题,因此您需要熟悉 DispatchQueues。
操作
CloudKit 交互是基于操作的。查询和写入被定义为Operation 的子类。定义每个任务,然后将其添加到要处理的队列中。这些操作是异步执行的。我发现将这种编程风格扩展到应用程序的其他区域很有用。
例如:当我创建一个新的Recipe 时,即针对Core Data 的Operation,然后我有一个Operation 在CloudKit 中创建该记录。第二个操作依赖于第一个,如果第一个不成功则取消。
将 Core Data 和 CloudKit 之间的协调视为一系列需要执行的任务变得容易一些。更改通知会发送到您的应用 > 获取更改(使用更改令牌)> 在 Core Data > 更新 UI(自动/手动)中添加/更新这些记录。每一个都是链条中的一个环节,并依赖于最后一个要完成的工作,然后再继续前进。
您可以使用基础Operation 和OperationQueue 或ProcedureKit 之类的框架来帮助完成此过程。
总的来说,Core Data 和 CloudKit 很好地集成在一起,并且通常用于您想要实现的场景类型。根据您所处的开发复杂性或状态,NSPersistentCloudKitContainer 可能是一个很好的起点。但是,一旦您超越了它的功能,您就会想要了解上面指出的挑战和功能。