SwiftData 教程与示例:添加 CloudKit 云同步(六)

了解如何为你的 iOS 应用添加 CloudKit 集成,并使用 iCloud 自动云同步你的应用数据。

SwiftData 教程与示例:添加 CloudKit 云同步(六)

如果你已经使用 SwiftData 完成了持久化数据,你就可以使用 iCloud 轻松地将你的应用数据备份到云端,防止在用户卸载应用之后丢失本地数据。

你还可以使用 iCloud 将这些数据同步到多部设备上。

为项目添加 iCloudKit 服务

在 Xcode 中,导航到 signing & Capabilities 中,点击左上角的 + Capability:

在打开页面中找到 iCloud 并添加它。在 Services 中,勾选 CloudKit 选项:

为 CloudKit 服务创建容器

添加 CloudKit 服务后,要使用 CloudKit 服务,必须添加 Container:

Container 是一个存储区域,用来保存和管理你的应用在 iCloud 上的所有数据。每个 Container 就像是一个数据库,可以在不同设备之间同步用户数据或让多个用户共享数据。

每个 CloudKit Container 都需要一个唯一的标识符(名称),推荐设置为与你 App 的 Bundle Identifier 一致即可,Xcode 会自动为其添加 iCloud. 前缀:

为项目添加 Remote notifications 服务

CloudKit 使用 Remote Notifications (推送通知服务)来保持设备间的数据同步。要使用 CloudKit 的 Remote Notifications ,必须在项目 Info.plist 中启用 remote-notification 后台模式。

和添加 CloudKit 方法类似,在 + Capability 中搜索「Background Modes」并添加,然后在 background Modes 中勾选「Remote notifications」:

完成 CloudKit 服务集成

完成上述配置之后,你已经完成了所有的配置工作,没错,就是这么简单。

但是由于 CloudKit 对 SwiftData 数据模型有非常严格的规范要求,你之前定义的数据模型在设备本地存储可能没有问题,但在启用 CloudKit 之后可能会遇到崩溃、卡死的问题。

你可以参考下面【常见错误与解决方案】部分来调整你的代码。

如果你已经修复了所有报错,在成功运行 App 之后,会在 Xcode 中看到大量与 CloudKit 相关的 DEBUG 信息输出:

这些日志是 SwiftData 与 CloudKit 集成的正常调试输出,显示了数据同步、CloudKit 配置、自动导入/导出等过程。如果您看到了这些输出,说明应用的 iCloud/CloudKit 同步功能正在正常工作。

CloudKit 后续工作

禁用 CloudKit 调试日志

为应用集成 CloudKit 服务之后,每次运行 App 时,默认 CloudKit 会将所有事件记录到控制台,这会严重妨碍其他调试信息:

通过在 Xcode 启动项中,添加以下两行记录,可以减少输出:

  • 禁用 CoreData 调试输出
-com.apple.CoreData.Logging.stderr 0
  • 禁用 CloudKit 调试输出
-com.apple.CoreData.CloudKitDebug 0
Xcode > Products > Scheme > Edit scheme > Arguments

获取 CloudKit 数据同步状态

  • 如何检测带有 CloudKit 的 SwiftData 是否已完成从其他地方同步新数据?
  • 如何检测是否还有更改需要同步?
  • 如何获取何时进行了 CloudKit 数据同步?

由于 SwiftData 是新的框架,截止 2024 年 10 月,Apple 尚未提供相关的接口用于获取以上数据。

常见错误与解决方案

缺少逆向关系(all relationships have an inverse)

运行 App 出现以下错误提示:

CloudKit integration requires that all relationships have an inverse, the following do not:

这是因为 CloudKit 要求 SwiftData 模型中的所有关系都有逆向关系。这对于维护数据的一致性和设备间的同步是必要的。

可以参考提示找到是哪些 @Relationship 尚未设置 inverse 参数,并添加上即可。

可以参考这篇文章中对于「逆向关系」的详细讲解:

SwiftData 教程与示例:定义 Relationship 关系(三)
了解如何使用 SwiftData 中的 @Relationship 宏来定义不同数据模型之间的关联方式,例如删除规则、反向引用等。

缺少默认值的非可选属性(all attributes be optional

运行 App 出现以下错误提示:

CloudKit integration requires that all attributes be optional, or have a default value set. The following attributes are marked non-optional but do not have a default value:
all attributes be optional

CloudKit 要求 SwiftData 中所有数据模型的数据必须设置为可选或者设置默认值

非可选的关系(all relationships be optional)

运行 App 出现以下错误提示:

CloudKit integration requires that all relationships be optional, the following are not:
all relationships be optional

CloudKit 要求所有关系都是可选的,以防止在同步时缺少相关对象导致的问题。

可以参考我这篇文章中,关于【设置关系属性为可选】部分的讲解:

SwiftData 教程与示例:最佳实践(七)
避免在异步任务中传递 modelContext 如果在异步任务中传递 modelContext,会遇到 Xcode 提示如下错误: SwiftData.ModelContext: Unbinding from the main queue. This context was instantiated on the main queue but is being used off it. ModelContexts are not Sendable, consider using a ModelActor. 这是由于 ModelContext 不支持并发访问。 ModelContext 不是 Sendable,不能在异步函数中跨线程传递,否则可能导致数据竞争。 将属性设置为可选或添加默认值 如果计划使用 CloudKit 备份与同步数据,CloudKit 要求

不支持的唯一约束(not support unique constraints)

运行 App 出现以下错误提示:

CloudKit integration does not support unique constraints. The following entities are constrained:
not support unique constraints

CloudKit 不支持在 SwiftData 模型中定义的唯一约束。唯一约束可能会在数据同步时导致冲突。

可以参考我这篇文章中,关于【避免使用唯一约束】部分的讲解:

SwiftData 教程与示例:最佳实践(七)
避免在异步任务中传递 modelContext 如果在异步任务中传递 modelContext,会遇到 Xcode 提示如下错误: SwiftData.ModelContext: Unbinding from the main queue. This context was instantiated on the main queue but is being used off it. ModelContexts are not Sendable, consider using a ModelActor. 这是由于 ModelContext 不支持并发访问。 ModelContext 不是 Sendable,不能在异步函数中跨线程传递,否则可能导致数据竞争。 将属性设置为可选或添加默认值 如果计划使用 CloudKit 备份与同步数据,CloudKit 要求

更多 SwiftData 教程

SwiftData 教程与实例:简化 iOS 开发数据持久化(一)
SwiftData 是 Apple 为 iOS 开发者提供的新型类 ORM 工具,简化数据库管理,无需编写 SQL,本文包含 SwiftData 教程和使用实例。
SwiftData 教程:通过 Swift 简化数据持久化(二)
了解如何使用 @Model 和 @Attribute 来定义数据模型,并介绍 SwiftData 中的关系处理与 CRUD 操作。
SwiftData 教程与示例:定义 Relationship 关系(三)
了解如何使用 SwiftData 中的 @Relationship 宏来定义不同数据模型之间的关联方式,例如删除规则、反向引用等。
SwiftData 教程与示例:数据结构变更引发的崩溃问题及解决方案(五)
了解如何通过使用模型初始化器、计算属性和数据迁移策略,解决 SwiftData 数据结构变更(如新增或删除字段)引发的应用崩溃问题。
SwiftData 教程与示例:最佳实践(七)
避免在异步任务中传递 modelContext 如果在异步任务中传递 modelContext,会遇到 Xcode 提示如下错误: SwiftData.ModelContext: Unbinding from the main queue. This context was instantiated on the main queue but is being used off it. ModelContexts are not Sendable, consider using a ModelActor. 这是由于 ModelContext 不支持并发访问。 ModelContext 不是 Sendable,不能在异步函数中跨线程传递,否则可能导致数据竞争。 将属性设置为可选或添加默认值 如果计划使用 CloudKit 备份与同步数据,CloudKit 要求
Configuring iCloud services | Apple Developer Documentation
Share user or app data among multiple instances of your app running on different devices.