字节笔记本
2026年2月23日
SwiftData 与 CloudKit 集成实战指南
SwiftData 与 CloudKit 集成实战指南,介绍如何配置 iCloud 同步、修复模型兼容性问题,以及实现实时数据更新。本文基于 WWDC23 的实践经验,包含完整的代码示例。
准备工作
SwiftData 与 NSPersistentCloudKitContainer 类似(实际上底层就是基于它封装),因此配置 iCloud 同步非常简单,只需几个步骤即可开始。
首先确保已启用 iCloud entitlements,并勾选 CloudKit 选项。你需要有一个配置好的容器。
同时开启后台模式和远程通知,否则容器将无法正常加载。
完成这些配置后,CloudKit 同步会自动工作。SwiftData 会从 entitlements 中检测到配置并自动使用。
如需更精确的控制,可以在 ModelConfiguration 中显式指定容器标识符:
@main
struct Brew_BookApp: App {
let container: ModelContainer = {
// 生产环境不要强制解包 👀
try! ModelContainer(
for: [Brewer.self, Brew.self],
.init(
cloudKitContainerIdentifier: "icloud.uk.co.alexanderlogan.samples.Brew-Book"
)
)
}()
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container)
}
}修复模型兼容性问题
启用 CloudKit 后,你的应用可能会突然崩溃。容器可能无法加载,控制台会充满错误信息。
典型的错误信息如下:
error: Store failed to load.
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:
Brew: brewDate
Brew: brewIdentifier
Brew: rating
Brew: type
CloudKit integration requires that all relationships be optional, the following are not:
Brewer: brews
CloudKit integration does not support unique constraints.
The following entities are constrained:
Brew: brewIdentifier与 NSPersistentCloudKitContainer 一样,我们需要对数据模型进行一些修改才能与云端正常工作。
主要注意事项:
- 唯一约束不受支持 - 需要移除
@Attribute(.unique) - 关系必须是可选的 - 即使默认值为空数组
- 所有属性必须有默认值 - 显式设置或使用可选类型
模型改造示例
Brewer 模型改造:
// 改造前
@Model
final class Brewer {
var name: String
@Relationship(.cascade, inverse: \Brew.brewer)
var brews: [Brew]
init(name: String) {
self.name = name
}
}// 改造后
@Model
final class Brewer {
var name: String = ""
@Relationship(.cascade, inverse: \Brew.brewer)
var brews: [Brew]? = []
init(name: String) {
self.name = name
}
}Brew 模型改造:
// 改造前
@Model
final class Brew {
@Attribute(.unique) var brewIdentifier: UUID
var type: BrewType.RawValue
var rating: Int
var brewDate: Date
var brewer: Brewer?
init(
brewIdentifier: UUID = .init(),
type: BrewType,
rating: Int,
brewDate: Date
) {
self.brewIdentifier = brewIdentifier
self.type = type.rawValue
self.rating = rating
self.brewDate = brewDate
}
}// 改造后
@Model
final class Brew {
var brewIdentifier: UUID = .init()
var type: BrewType.RawValue = BrewType.espresso.rawValue
var rating: Int = 5
var brewDate: Date = Date()
var brewer: Brewer? = nil
init(
brewIdentifier: UUID = .init(),
type: BrewType,
rating: Int,
brewDate: Date
) {
self.brewIdentifier = brewIdentifier
self.type = type.rawValue
self.rating = rating
self.brewDate = brewDate
}
}完成这些修改后,在已登录 iCloud 的设备上运行应用,保存一些数据,然后删除应用并重新安装,内容将会从云端恢复!
实现实时更新
你可能会注意到,即使云端有内容,应用首次打开时也不会显示任何内容。这是因为 @Query 在视图安装时不会自动响应来自云端的推送通知。
不过有一个巧妙的解决方法:利用底层的 NSPersistentCloudKitContainer.eventChangedNotification 来检测 CloudKit 更新事件,并强制刷新查询。
在 brewers 列表中添加以下功能:
.onReceive(NotificationCenter.default.publisher(
for: NSPersistentCloudKitContainer.eventChangedNotification
)) { notification in
guard let event = notification.userInfo?[
NSPersistentCloudKitContainer.eventNotificationUserInfoKey
] as? NSPersistentCloudKitContainer.Event else {
return
}
if event.endDate != nil && event.type == .import {
// TODO
}
}这里我们监听 eventChangedNotification,检查事件类型是否为 import(数据下载),并且事件已经结束。
现在来看关键技巧:
当这个通知触发时,数据已经下载完成,但 @Query 不会自动更新。我们可以通过手动执行一次 fetch 来"欺骗"查询更新:
.onReceive(NotificationCenter.default.publisher(
for: NSPersistentCloudKitContainer.eventChangedNotification
)) { notification in
guard let event = notification.userInfo?[
NSPersistentCloudKitContainer.eventNotificationUserInfoKey
] as? NSPersistentCloudKitContainer.Event else {
return
}
if event.endDate != nil && event.type == .import {
Task { @MainActor in
let brewersFetchDescriptor = FetchDescriptor<Brewer>(
predicate: nil,
sortBy: [.init(\.name)]
)
_ = try? context.fetch(brewersFetchDescriptor)
}
}
}当这个 fetch 执行时,它会同时触发 @Query 的更新。现在,当你全新安装应用时,内容会从云端获取并按预期更新,后续的更新也会正常工作。
总结
SwiftData 与 CloudKit 的集成大大简化了 iOS 应用的云端数据同步。主要要点:
- 配置简单 - 只需开启 entitlements 和后台模式
- 模型限制 - 移除唯一约束,设置默认值,使用可选关系
- 实时更新 - 利用
eventChangedNotification实现数据自动刷新
完整的示例代码可以在 GitHub 上找到。
原文作者:Alex Logan (@SwiftyAlex) 原文链接:https://alexanderlogan.co.uk/blog/wwdc23/08-cloudkit-swift-data