# 用 CloudKit 同步 SwiftData > 让 SwiftData 通过 CloudKit 同步:所需能力、ModelConfiguration、CloudKit 对模型的要求、把架构部署到生产环境,以及只同步到私有数据库。 - Canonical page: https://obelisk.club/blog/swiftdata-cloudkit-sync-zh - Author: Peter Zhang - Published: 2026-11-01 - Tags: swiftdata, cloudkit, icloud, sync, swift, ios, indie, 中文 要让 SwiftData 通过 iCloud 同步,需要给 App 添加两项能力(带 CloudKit 和容器的 iCloud,以及勾选了远程通知的后台模式),让模型符合 CloudKit 的要求,再运行 App 创建开发架构(schema)。发布前,要在 CloudKit Console 里把这个架构部署到生产环境,因为 App Store 上的版本只能访问生产环境。大部分工作都在模型上:不能有唯一性约束,关系必须是可选的,每个属性都要有值。想在浏览器里对照这些规则检查一份模型文件,可以用我们的 [SwiftData 与 CloudKit 检查工具](/tools/swiftdata-cloudkit-checker)。 Apple 的文章说,SwiftData 使用 Core Data 的 `NSPersistentCloudKitContainer` 来完成 CloudKit 同步,并通过读取 App 的授权(entitlements)来推断 CloudKit 配置。 ## 添加所需能力 Apple 列出了 SwiftData 做 iCloud 同步所需的两项能力: 1. **iCloud。** 打开 CloudKit,并创建或选择一个容器。Apple 把容器描述为 CloudKit 用来在 iCloud 服务器上隔离你 App 的数据库的对象。这项能力需要有管理员权限的 Apple Developer 账号。 2. **后台模式(Background Modes)。** 勾选"Remote notifications"选项。系统会以静默方式递送这些通知,SwiftData 用它们得知其他设备上发生的变化并加以应用。 ## 在 ModelConfiguration 里选择 CloudKit 数据库 `ModelConfiguration`(iOS 17、macOS 14)有一个类型为 `ModelConfiguration.CloudKitDatabase` 的 `cloudKitDatabase` 参数,它有三种取值: - **默认值。** SwiftData 读取 `Entitlements.plist`,选用找到的第一个容器标识符。不传这个参数时就是这种行为。 - **`.private("iCloud.com.example.Trips")`。** 指定一个具体的容器。Apple 建议在 App 使用多个 CloudKit 容器时这样做。 - **`.none`。** 关闭 SwiftData 的同步,覆盖它本来会找到的任何容器。Apple 给出的用途是:App 已经以自己的方式使用 CloudKit,架构可能互相冲突。 ```swift import SwiftUI import SwiftData @main struct TripsApp: App { let modelContainer: ModelContainer init() { let config = ModelConfiguration(cloudKitDatabase: .private("iCloud.com.example.Trips")) do { modelContainer = try ModelContainer(for: Trip.self, Stay.self, configurations: config) } catch { fatalError(error.localizedDescription) } } var body: some Scene { WindowGroup { Text("Trips") } .modelContainer(modelContainer) } } func localOnlyContainer() throws -> ModelContainer { let config = ModelConfiguration(cloudKitDatabase: .none) return try ModelContainer(for: Trip.self, Stay.self, configurations: config) } func defaultContainer() throws -> ModelContainer { try ModelContainer(for: Trip.self, Stay.self) } ``` Apple 还提醒:如果你的 App 已经有生产环境的 CloudKit 架构,只能指定之前由 SwiftData 或 Core Data 管理过的容器,其他容器都不兼容。 ## 让模型符合 CloudKit 的要求 SwiftData 的宏生成的架构在很多情况下本来就兼容 CloudKit,但有少数功能无法带过去。下表把 Apple 列出的内容和另外一条规则放在一起。 | 模型中的写法 | CloudKit 的要求 | |---|---| | `@Attribute(.unique)` | 不要使用。CloudKit 无法保证唯一性,因为变化是并发地、在合适的时机同步的。 | | 关系(relationship) | 每个关系都必须是可选的。iCloud 服务器不保证以原子方式处理关系的变化。 | | 反向关系(inverse) | 只有 SwiftData 能从架构中可靠地推断出反向关系时才会替你设置。否则要在保存前显式设置,因为 CloudKit 处理变化的顺序是不确定的。 | | 删除规则 `.deny` | 不支持,因为变化不会立即同步。请改用其他规则,比如 `.cascade` 或 `.nullify`。 | | 属性(attribute) | 给每个属性一个默认值,或者设为可选(见下文)。 | 最后一行不在 Apple 的 SwiftData 页面上。我们是在用 SwiftData 模型加载一个开启了 CloudKit 的 Core Data 存储时看到的:它报错说"CloudKit integration requires that all attributes be optional, or have a default value set."。在我们的测试里,只在 `init()` 内部给的默认值不算数,所以请把默认值直接写在属性上。 ```swift @Model final class Trip { var name: String = "" var startDate: Date = Date() var notes: String? @Relationship(deleteRule: .cascade, inverse: \Stay.trip) var stays: [Stay]? init(name: String) { self.name = name } } @Model final class Stay { var place: String = "" var trip: Trip? init(place: String) { self.place = place } } ``` 如果你要迁移的是已有的 Core Data 应用,可以用我们的 [Core Data 转 SwiftData 工具](/tools/core-data-to-swiftdata) 生成模型,再用上面的检查工具按这份清单核对。Apple 关于 Core Data 与 CloudKit 的文章还要求所有关系都有反向关系,并且同一个配置(configuration)里的实体不能与另一个配置里的实体建立关系。 ## 创建开发架构 开发期间,CloudKit 在开发环境里有一份草稿架构,可以随意重写。Apple 的文章展示了如何在调试构建里创建它:先建一个 `ModelConfiguration`,把 `NSPersistentStoreDescription` 指向它的 `url`,设置容器标识符,同步加载存储,用你的 SwiftData 类生成一个托管对象模型,然后在 `NSPersistentCloudKitContainer` 上调用 `initializeCloudKitSchema()`。最后在 SwiftData 自己的容器加载之前把这个存储移除,避免两者同时同步。 ```swift import CoreData #if DEBUG func initializeSchema() throws { let config = ModelConfiguration() try autoreleasepool { let desc = NSPersistentStoreDescription(url: config.url) desc.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.com.example.Trips") desc.shouldAddStoreAsynchronously = false if let mom = NSManagedObjectModel.makeManagedObjectModel(for: [Trip.self, Stay.self]) { let container = NSPersistentCloudKitContainer(name: "Trips", managedObjectModel: mom) container.persistentStoreDescriptions = [desc] container.loadPersistentStores { _, err in if let err { fatalError(err.localizedDescription) } } try container.initializeCloudKitSchema() if let store = container.persistentStoreCoordinator.persistentStores.first { try container.persistentStoreCoordinator.remove(store) } } } } #endif ``` 像 Apple 那样用 `#if DEBUG` 包起来,确保它不会在发布版本里运行。之后打开 CloudKit Console 查看你的记录类型。如果看不到,Apple 说你可能需要为它们开启查询支持。 ## 把架构部署到生产环境 App Store 上的 App 只能访问生产环境,Apple 说你必须在发布之前把开发架构部署过去。在 CloudKit Console 中: 1. 在 icloud.developer.apple.com 登录,打开 CloudKit Database 应用。 2. 选择你的容器。 3. 选择 Deploy Schema Changes。 4. 检查待部署的变更,点击 Deploy。 部署会复制记录类型、字段和索引,但不会复制记录。你需要有编辑生产环境的权限:个人开发者本人就有,团队里则由管理员来做这一步,或者授予你该权限。 此后架构只能增量变化。你可以添加记录类型和字段,但不能删除或修改已经在生产环境里的记录类型和字段。Apple 给出的后续改动策略有:把用户迁移到新容器里的新存储;逐步添加字段,这样旧版本 App 仍能看到每条记录(但不是每个字段);或者保留一个 `version` 属性,只获取当前版本的 App 看得懂的记录。设计第一版生产架构时要想到这一点:现在起的名字就改不了了。 ## 只同步到私有数据库 看看上面的取值:默认值、`.private(_:)` 和 `.none`。SwiftData 的 `CloudKitDatabase` 没有公共或共享数据库的选项。Apple 的 Core Data 设置文章讲的是 SwiftData 所依托的机制,其中提到本地存储"由 CloudKit 私有数据库支持"。实际上,这意味着它在同一个人自己的设备之间、通过其自己的 iCloud 账号同步,设计功能时请以此为前提。Apple 的 SwiftData 页面没有谈到在不同人之间共享记录,所以我们不写这部分。 ## 来源 以下页面均阅读于 2026 年 11 月 1 日。 - [Syncing model data across a person's devices](https://developer.apple.com/documentation/swiftdata/syncing-model-data-across-a-persons-devices)(SwiftData) - [ModelConfiguration](https://developer.apple.com/documentation/swiftdata/modelconfiguration) 与 [ModelConfiguration.CloudKitDatabase](https://developer.apple.com/documentation/swiftdata/modelconfiguration/cloudkitdatabase-swift.struct) - [Deploying an iCloud Container's Schema](https://developer.apple.com/documentation/cloudkit/deploying-an-icloud-container-s-schema)(CloudKit) - [Creating a Core Data Model for CloudKit](https://developer.apple.com/documentation/coredata/creating-a-core-data-model-for-cloudkit) - [Setting Up Core Data with CloudKit](https://developer.apple.com/documentation/coredata/setting-up-core-data-with-cloudkit) *Apple 的要求可能变化,请以上述页面的最新内容为准。*