跳到正文

← 全部工具

给 Apple 开发者的免费检查工具

SwiftData CloudKit 同步检查

SwiftData 要通过 iCloud 同步,Apple 的规则是:不能有唯一约束(@Attribute(.unique) 或 #Unique),所有关系都必须可选并有反向关系,不能用 .deny 删除规则。负责同步的 Core Data 还会拒绝加载这样的存储:某个属性不是可选的,声明处也没有默认值。在下方粘贴你的 @Model 类,即可看到每个问题、对应的 Apple 页面和修改后的代码行。检查器下方是 Apple 列出的设置步骤(带 CloudKit 容器的 iCloud、Background Modes 中的 Remote notifications、ModelConfiguration 的 cloudKitDatabase),代码可直接复制。

检查你的 @Model 类

粘贴一个或多个 SwiftData 模型类。检查在本页内完成,不上传任何内容。

在上方粘贴 @Model 类,即可看到哪些地方会让它们无法通过 iCloud 同步。

Apple 对 iCloud 同步模型的规则

SwiftData 的同步与 Core Data 使用同一套 CloudKit 机制,所以 Core Data 的规则同样适用。

Apple 列出的设置步骤

来自 Apple 关于同步 SwiftData 模型数据的文章。代码是 Apple 的示例,请换成你自己的容器标识符和模型类型。 SwiftData:Syncing model data across a person's devices

1. 添加 iCloud 功能并启用 CloudKit

在 Xcode 中打开目标的 Signing & Capabilities,点 + Capability 添加 iCloud。勾选 CloudKit,然后在 Containers 下添加或选择一个容器(标识符以 iCloud. 开头)。iCloud 功能需要有管理员权限的开发者账号。

2. Background Modes:Remote notifications

添加 Background Modes 功能并勾选 Remote notifications。服务器上的数据变化时,CloudKit 会发送静默通知,SwiftData 据此拉取这些更改。

3. 指定容器(可选)

默认情况下,SwiftData 使用 entitlements 中列出的第一个 CloudKit 容器。如需自己指定,用 cloudKitDatabase 传入:

let config = ModelConfiguration(cloudKitDatabase: .private("iCloud.com.example.Trips"))
let modelContainer = try ModelContainer(for: Trip.self, Accommodation.self,
                                        configurations: config)

关闭同步

如果 App 已经把 CloudKit 用在别处,而这些模型只应保存在设备上,传入 .none:

let config = ModelConfiguration(cloudKitDatabase: .none)
let modelContainer = try ModelContainer(for: Trip.self, Accommodation.self,
                                        configurations: config)

4. 创建开发环境架构

Apple 的示例只在 Debug 构建中初始化 CloudKit 开发架构,然后再打开 SwiftData 容器。完成后在 CloudKit Console 中查看结果。

let config = ModelConfiguration()

do {
#if DEBUG
    // Use an autorelease pool to make sure Swift deallocates the persistent
    // container before setting up the SwiftData stack.
    try autoreleasepool {
        let desc = NSPersistentStoreDescription(url: config.url)
        let opts = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.com.example.Trips")
        desc.cloudKitContainerOptions = opts
        // Load the store synchronously so it completes before initializing the
        // CloudKit schema.
        desc.shouldAddStoreAsynchronously = false
        if let mom = NSManagedObjectModel.makeManagedObjectModel(for: [Trip.self, Accommodation.self]) {
            let container = NSPersistentCloudKitContainer(name: "Trips", managedObjectModel: mom)
            container.persistentStoreDescriptions = [desc]
            container.loadPersistentStores {_, err in
                if let err {
                    fatalError(err.localizedDescription)
                }
            }
            // Initialize the CloudKit schema after the store finishes loading.
            try container.initializeCloudKitSchema()
            // Remove and unload the store from the persistent container.
            if let store = container.persistentStoreCoordinator.persistentStores.first {
                try container.persistentStoreCoordinator.remove(store)
            }
        }
    }
#endif
    modelContainer = try ModelContainer(for: Trip.self, Accommodation.self,
                                        configurations: config)
} catch {
    fatalError(error.localizedDescription)
}

5. 发布前把架构推到生产环境

上架前在 CloudKit Console 中把开发架构发布到生产环境。此后只能新增模型类型和属性。

来源

Apple 官方页面,查阅日期 2026 年 11 月 1 日:

工作原理

  • •页面读取你的 Swift 文本,找出每个标有 @Model 的类、其中的存储属性、类型和默认值,以及 @Attribute、@Relationship 和 #Unique 里的选项。计算属性、静态属性和 @Transient 属性会跳过。
  • •每个属性都按 Apple 的 SwiftData 同步文章和 Core Data CloudKit 模型页面中的规则检查。每条结果写明规则、链接到 Apple 页面,并给出修改后的代码行;列表下方是应用全部修改后的完整模型。
  • •检查反向关系时会看两边:只要有一边在 inverse: 中写明另一边,这一对就没问题。如果没有属性指回来,修改方案会在另一个模型中加一个。
  • •这些规则已用 Core Data 本身核对过:在 macOS 上用 NSPersistentCloudKitContainer 加载一个包含各类问题的示例,Core Data 列出的问题与本页找到的一致;修改后存储加载没有任何错误。修改后的模型已用 iOS 27.1 SDK、iOS 17 目标做过类型检查。
  • •规则和设置步骤来自下方“来源”列出的 Apple 页面,查阅日期为 2026 年 11 月 1 日。

能做什么,不能做什么

  • •这是针对常见写法的文本解析器,不是 Swift 编译器。少见的写法,例如一行声明多个属性、自定义宏、通过 typealias 定义的类型,可能读错或漏掉。
  • •它只认识你粘贴进来的模型。如果属性的类型是没有粘贴的模型,又没有写 @Relationship,会被当作普通属性。请把相关模型一起粘贴。
  • •不检查 entitlements、功能配置、CloudKit 容器或 CloudKit Console。这些请按下方的设置步骤操作。
  • •它不知道你的生产环境 CloudKit 架构里已有什么,因此无法判断某项改动在那里是否还允许,只会提醒用 originalName: 写的重命名。
  • •改成可选是稳妥的修改方式,但读取它的代码需要相应修改;你自己选的默认值也可能比这里给出的更适合你的数据。
  • •不检查配置(configuration)划分、记录大小上限和设备之间的冲突。

常见问题

为什么 CloudKit 下不能用 @Attribute(.unique)?
Apple 的 SwiftData 同步文章说明,各设备上的更改在不同时间同步,CloudKit 无法保证唯一选项生效;Core Data 的 CloudKit 页面也写明不支持唯一约束。请去掉 .unique(以及 #Unique),在同步后用自己的代码处理重复项,例如合并标识符相同的记录。
一对多关系也必须是可选的吗?
是的。Apple 说明 CloudKit 要求所有关系都是可选的,而 var items: [Item] = [] 这样的非可选数组算作必填,Core Data 加载存储时会报出来。请写成 var items: [Item]? = [],读取时用 items ?? []。
init() 里已经赋值了,为什么还要默认值?
init() 中赋的值不属于模型架构。Core Data 的 CloudKit 设置会检查架构,拒绝任何既不可选、也没有默认值的属性。请在声明处给默认值,例如 var name: String = "",或者改为可选。
关系的两边都要写 inverse: 吗?
不用,只写一边。Apple 说明 SwiftData 能从架构中可靠推断时会设定反向关系,否则需要你明确写出。当一个模型有多个关系指向同一个模型时就无法推断,所以每一对都在一边写明最稳妥。
SwiftData 的 iCloud 同步需要哪些功能配置?
按 Apple 的说明需要两项:iCloud(勾选 CloudKit 并选择容器),以及 Background Modes(勾选 Remote notifications,让 CloudKit 通知 App 服务器上的变化)。默认情况下 SwiftData 使用 entitlements 中的第一个 CloudKit 容器;用 ModelConfiguration(cloudKitDatabase: .private("iCloud.…")) 可指定容器,传 .none 则数据只留在设备上。
App 上架后还能改模型吗?
只能新增。CloudKit 架构发布到生产环境后,Apple 说明不能删除模型类型,也不能修改已有属性;Core Data 的页面还补充了记录类型和字段不能改名。新属性请设为可选或给默认值,旧属性保留不动。

用到这个工具的指南

相关工具