CloudKit 是什么
CloudKit 是苹果的云数据库服务,直接把 iCloud 能力交给 App:存储结构化记录、文件、键值数据,还能跨设备实时同步。它免后端搭建,适合个人数据类应用。
用户的数据跟着 iCloud 走,App 只管读写。
三种存储:私有库(用户私有,容量随 iCloud 套餐)、共享库(多人协作)、公共库(所有用户共享,有配额)。记笔记、记账、待办这类 App,私有库是主场。
容器与数据库
先在开发者后台建 Container,代码里用 CKContainer 拿到数据库。默认的 defaultContainer 对应主容器,多容器场景逐个初始化。
let container = CKContainer.default()
let privateDB = container.privateCloudDatabase
// 记录类型在 CloudKit Console 里定义好字段
- CKRecord一条记录。
- CKRecordZone记录分区。
- CKQuery查询。
- CKSubscription变更订阅。
记录类型(如 Note、Task)在 Console 定义,字段类型有 String、Int、Asset 等。Schema 变更要小心,线上改类型可能报错,开发期尽早定型。
记录与查询
保存记录用 save,查询用 CKQuery 加谓词。CloudKit 的查询是异步的,配合 async/await 写起来很顺。
let record = CKRecord(recordType: "Note")
record["title"] = "标题"
record["content"] = "内容"
try await privateDB.save(record)
let predicate = NSPredicate(format: "title CONTAINS %@", "云")
let query = CKQuery(recordType: "Note", predicate: predicate)
let result = try await privateDB.records(matching: query)
查询结果分页用 CKQueryOperation 的 cursor 继续拉。复杂筛选尽量在谓词里做,客户端过滤数据多时性能差。
写查询前先想清楚索引:CloudKit 的查询性能依赖字段索引,Console 里给常用筛选字段建索引,查询才会快。复杂查询拆成几次简单查询,别指望一条大查询包打天下。记录大小也有限制,大文件(图片、音频)用 CKAsset 存,字符串和二进制数据别硬塞进记录字段。批量操作时一次处理一批,配合 modifyRecords 批量保存或删除,网络往返少一大半。
数据安全要守住两条线:私有库的数据权限由 iCloud 账号天然隔离,但应用内仍要做账号维度校验;公共库写入要谨慎,别把用户隐私写进公共区域。
订阅与推送
订阅让云端在记录变化时推送通知:别的设备改了数据,这台设备能立刻知道。订阅分查询订阅和记录区域订阅。
let sub = CKQuerySubscription(
recordType: "Note",
predicate: NSPredicate(value: true),
subscriptionID: "note-changes",
options: [.firesOnRecordCreation, .firesOnRecordUpdate, .firesOnRecordDeletion]
)
sub.notificationInfo = CKNotificationInfo()
sub.notificationInfo?.alertBody = "数据已更新"
try await privateDB.save(sub)
收到远程通知后,解析 CKQueryNotification 里的 recordID,再去拉最新数据更新本地。订阅 ID 全局唯一,重复创建会报错,先查再建。
推送到达率不是百分之百,应用启动时还要主动拉一次最新数据兜底,别只依赖推送通知;同时做好幂等处理,同一批更新无论收到几次,本地结果一致。通知负载里只放记录 ID 和变更类型,正文数据走按需拉取,避免推送体积撑爆限制。
离线与缓存
CloudKit 只保证在线读写,离线体验要靠本地缓存:本地存一份数据,在线时同步增量。SwiftData/Core Data 做本地,CloudKit 做云端,两套映射。
// 简化思路:本地主、云为辅
// 本地写入成功 -> 后台推送到 CloudKit
// 云端通知 -> 拉取变更 -> 更新本地
func syncDown(recordID: CKRecord.ID) async throws {
let record = try await privateDB.record(for: recordID)
// 更新本地模型
}
NSPersistentCloudKitContainer 是现成的桥:Core Data 和 CloudKit 自动双向同步,省掉手写映射。数据量大的场景按需拉取,别全量下载。
多端同步实战
把笔记 App 做成多端同步:本地 SwiftData 存全量,CloudKit 存变更,订阅通知触发增量刷新。冲突用 updatedAt 时间戳兜底,谁后写谁赢。
// 保存时记录修改时间
record["updatedAt"] = Date()
// 拉取时按时间过滤
let predicate = NSPredicate(format: "updatedAt > %@", lastSyncTime as NSDate)
let query = CKQuery(recordType: "Note", predicate: predicate)
上线前在 CloudKit Console 观察请求量和错误率。网络差时排队重试,用指数退避避免打爆配额。别忘了在 Info.plist 加 CloudKit 权限描述。
小结
CloudKit 让 iCloud 成为 App 的免费后端:CKRecord 存数据、CKQuery 查数据、CKSubscription 感知变化、本地缓存撑离线。个人数据类应用用它省掉整套后端,多端同步开箱即用。把 schema 定稳、订阅建对、冲突规则想清楚,CloudKit 就是最省心的同步方案。