axiom-cloudkit-ref
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCloudKit Reference
CloudKit参考文档
Purpose: Comprehensive CloudKit reference for database-based iCloud storage and sync
Availability: iOS 10.0+ (basic), iOS 17.0+ (CKSyncEngine), iOS 17.0+ (SwiftData integration)
Context: Modern CloudKit sync via CKSyncEngine (WWDC 2023) or SwiftData integration
用途:针对基于数据库的iCloud存储与同步的全面CloudKit参考资料
适用版本:iOS 10.0+(基础功能),iOS 17.0+(CKSyncEngine),iOS 17.0+(SwiftData集成)
背景:通过CKSyncEngine(WWDC 2023)或SwiftData集成实现现代CloudKit同步
When to Use This Skill
何时使用本技能
Use this skill when:
- Implementing structured data sync to iCloud
- Choosing between SwiftData+CloudKit, CKSyncEngine, or raw CloudKit APIs
- Setting up public/private/shared databases
- Implementing conflict resolution
- Debugging CloudKit sync issues
- Monitoring CloudKit performance
NOT for: Simple file sync (use instead)
axiom-icloud-drive-ref在以下场景使用本技能:
- 实现结构化数据向iCloud的同步
- 在SwiftData+CloudKit、CKSyncEngine或原生CloudKit API之间做选择
- 配置公开/私有/共享数据库
- 实现冲突解决逻辑
- 调试CloudKit同步问题
- 监控CloudKit性能
不适用场景:简单文件同步(请使用技能)
axiom-icloud-drive-refOverview
概述
CloudKit is for STRUCTURED DATA sync (records with relationships), not simple file sync.
Three modern approaches:
- SwiftData + CloudKit (Easiest, iOS 17+)
- CKSyncEngine (Custom persistence, iOS 17+, WWDC 2023)
- Raw CloudKit APIs (Maximum control, more complexity)
CloudKit适用于结构化数据同步(带关联关系的记录),而非简单文件同步。
三种现代实现方式:
- SwiftData + CloudKit(最简单,iOS 17+)
- CKSyncEngine(自定义持久化,iOS 17+,WWDC 2023)
- 原生CloudKit API(最大控制权,复杂度更高)
Approach 1: SwiftData + CloudKit (Recommended)
方式1:SwiftData + CloudKit(推荐)
When to use: iOS 17+ apps with SwiftData models
Limitations:
- Private database only (no public/shared)
- Automatic sync (less control)
- SwiftData constraints apply
swift
// ✅ CORRECT: SwiftData with CloudKit sync
import SwiftData
@Model
class Task {
var title: String
var isCompleted: Bool
var dueDate: Date
init(title: String, isCompleted: Bool = false, dueDate: Date) {
self.title = title
self.isCompleted = isCompleted
self.dueDate = dueDate
}
}
// Configure CloudKit container
let container = try ModelContainer(
for: Task.self,
configurations: ModelConfiguration(
cloudKitDatabase: .private("iCloud.com.example.app")
)
)
// That's it! Sync happens automaticallyEntitlements required:
- iCloud capability
- CloudKit container
Use skill for SwiftData details
axiom-swiftdata适用场景:使用SwiftData模型的iOS 17+应用
限制:
- 仅支持私有数据库(无公开/共享数据库)
- 自动同步(控制权较少)
- 受SwiftData约束限制
swift
// ✅ 正确示例:带CloudKit同步的SwiftData实现
import SwiftData
@Model
class Task {
var title: String
var isCompleted: Bool
var dueDate: Date
init(title: String, isCompleted: Bool = false, dueDate: Date) {
self.title = title
self.isCompleted = isCompleted
self.dueDate = dueDate
}
}
// 配置CloudKit容器
let container = try ModelContainer(
for: Task.self,
configurations: ModelConfiguration(
cloudKitDatabase: .private("iCloud.com.example.app")
)
)
// 完成!同步会自动进行所需权限:
- iCloud功能权限
- CloudKit容器
如需了解SwiftData详情,请使用技能
axiom-swiftdataApproach 2: CKSyncEngine (Modern, WWDC 2023)
方式2:CKSyncEngine(现代方案,WWDC 2023)
When to use: Custom persistence (SQLite, GRDB, JSON) with cloud sync
Advantages over raw CloudKit:
- Manages fetch/upload cycles automatically
- Handles conflicts
- Manages account changes
- Recommended over manual CKDatabase operations
swift
// ✅ CORRECT: CKSyncEngine setup
import CloudKit
class SyncManager {
let syncEngine: CKSyncEngine
init() throws {
let config = CKSyncEngine.Configuration(
database: CKContainer.default().privateCloudDatabase,
stateSerialization: loadSyncState(),
delegate: self
)
syncEngine = try CKSyncEngine(config)
}
// Implement delegate methods
}
extension SyncManager: CKSyncEngineDelegate {
// Handle events
func handleEvent(_ event: CKSyncEngine.Event, syncEngine: CKSyncEngine) async {
switch event {
case .stateUpdate(let stateUpdate):
saveSyncState(stateUpdate.stateSerialization)
case .accountChange(let change):
handleAccountChange(change)
case .fetchedDatabaseChanges(let changes):
applyDatabaseChanges(changes)
case .fetchedRecordZoneChanges(let changes):
applyRecordChanges(changes)
case .sentRecordZoneChanges(let changes):
handleSentChanges(changes)
case .willFetchChanges, .didFetchChanges,
.willSendChanges, .didSendChanges:
// Optional lifecycle events
break
@unknown default:
break
}
}
// Next batch of changes to send
func nextRecordZoneChangeBatch(
_ context: CKSyncEngine.SendChangesContext,
syncEngine: CKSyncEngine
) async -> CKSyncEngine.RecordZoneChangeBatch? {
// Return pending local changes
let pendingChanges = getPendingLocalChanges()
return CKSyncEngine.RecordZoneChangeBatch(
pendingSaves: pendingChanges,
recordIDsToDelete: []
)
}
}Key concepts:
- State serialization: Persist sync state between app launches
- Events: Delegate receives events for changes
- Batches: You provide pending changes, engine uploads them
- Automatic conflict resolution: Engine handles basic conflicts
适用场景:自定义持久化(SQLite、GRDB、JSON)搭配云端同步
相比原生CloudKit的优势:
- 自动管理获取/上传周期
- 处理冲突问题
- 管理账户变更
- 推荐替代手动CKDatabase操作
swift
// ✅ 正确示例:CKSyncEngine配置
import CloudKit
class SyncManager {
let syncEngine: CKSyncEngine
init() throws {
let config = CKSyncEngine.Configuration(
database: CKContainer.default().privateCloudDatabase,
stateSerialization: loadSyncState(),
delegate: self
)
syncEngine = try CKSyncEngine(config)
}
// 实现代理方法
}
extension SyncManager: CKSyncEngineDelegate {
// 处理事件
func handleEvent(_ event: CKSyncEngine.Event, syncEngine: CKSyncEngine) async {
switch event {
case .stateUpdate(let stateUpdate):
saveSyncState(stateUpdate.stateSerialization)
case .accountChange(let change):
handleAccountChange(change)
case .fetchedDatabaseChanges(let changes):
applyDatabaseChanges(changes)
case .fetchedRecordZoneChanges(let changes):
applyRecordChanges(changes)
case .sentRecordZoneChanges(let changes):
handleSentChanges(changes)
case .willFetchChanges, .didFetchChanges,
.willSendChanges, .didSendChanges:
// 可选生命周期事件
break
@unknown default:
break
}
}
// 下一批待发送的变更
func nextRecordZoneChangeBatch(
_ context: CKSyncEngine.SendChangesContext,
syncEngine: CKSyncEngine
) async -> CKSyncEngine.RecordZoneChangeBatch? {
// 返回待处理的本地变更
let pendingChanges = getPendingLocalChanges()
return CKSyncEngine.RecordZoneChangeBatch(
pendingSaves: pendingChanges,
recordIDsToDelete: []
)
}
}核心概念:
- 状态序列化:在应用启动之间持久化同步状态
- 事件:代理接收变更事件
- 批次:你提供待处理变更,引擎负责上传
- 自动冲突解决:引擎处理基础冲突
Approach 3: Raw CloudKit APIs (Legacy)
方式3:原生CloudKit API(遗留方案)
When to use: Only if CKSyncEngine doesn't fit (rare)
Core types:
- — Entry point
CKContainer - — Public/private/shared scope
CKDatabase - — Individual data record
CKRecord - — Logical grouping
CKRecordZone - — Binary file storage
CKAsset
适用场景:仅当CKSyncEngine无法满足需求时使用(罕见情况)
核心类型:
- — 入口点
CKContainer - — 公开/私有/共享作用域
CKDatabase - — 单个数据记录
CKRecord - — 逻辑分组
CKRecordZone - — 二进制文件存储
CKAsset
Basic Operations
基础操作
swift
// ✅ Container and database
let container = CKContainer.default()
let privateDatabase = container.privateCloudDatabase
let publicDatabase = container.publicCloudDatabase
// ✅ Create record
let record = CKRecord(recordType: "Task")
record["title"] = "Buy groceries"
record["isCompleted"] = false
record["dueDate"] = Date()
// ✅ Save record
try await privateDatabase.save(record)
// ✅ Fetch record
let recordID = CKRecord.ID(recordName: "task-123")
let fetchedRecord = try await privateDatabase.record(for: recordID)
// ✅ Query records
let predicate = NSPredicate(format: "isCompleted == NO")
let query = CKQuery(recordType: "Task", predicate: predicate)
let (matchResults, _) = try await privateDatabase.records(matching: query)
for result in matchResults {
if case .success(let record) = result.1 {
print("Task: \(record["title"] as? String ?? "")")
}
}
// ✅ Delete record
try await privateDatabase.deleteRecord(withID: recordID)swift
// ✅ 容器与数据库
let container = CKContainer.default()
let privateDatabase = container.privateCloudDatabase
let publicDatabase = container.publicCloudDatabase
// ✅ 创建记录
let record = CKRecord(recordType: "Task")
record["title"] = "Buy groceries"
record["isCompleted"] = false
record["dueDate"] = Date()
// ✅ 保存记录
try await privateDatabase.save(record)
// ✅ 获取记录
let recordID = CKRecord.ID(recordName: "task-123")
let fetchedRecord = try await privateDatabase.record(for: recordID)
// ✅ 查询记录
let predicate = NSPredicate(format: "isCompleted == NO")
let query = CKQuery(recordType: "Task", predicate: predicate)
let (matchResults, _) = try await privateDatabase.records(matching: query)
for result in matchResults {
if case .success(let record) = result.1 {
print("Task: \(record["title"] as? String ?? "")")
}
}
// ✅ 删除记录
try await privateDatabase.deleteRecord(withID: recordID)Conflict Resolution
冲突解决
swift
// ✅ Handle conflicts with savePolicy
let operation = CKModifyRecordsOperation(
recordsToSave: [record],
recordIDsToDelete: nil
)
// Save only if server version unchanged
operation.savePolicy = .ifServerRecordUnchanged
// OR: Always overwrite server
operation.savePolicy = .changedKeys // Only changed fields
operation.modifyRecordsResultBlock = { result in
switch result {
case .success:
print("Saved")
case .failure(let error as CKError):
if error.code == .serverRecordChanged {
// Conflict - merge manually
let serverRecord = error.serverRecord
let clientRecord = error.clientRecord
let merged = mergeRecords(server: serverRecord, client: clientRecord)
// Retry with merged record
}
}
}
privateDatabase.add(operation)swift
// ✅ 使用savePolicy处理冲突
let operation = CKModifyRecordsOperation(
recordsToSave: [record],
recordIDsToDelete: nil
)
// 仅当服务器版本未变更时保存
operation.savePolicy = .ifServerRecordUnchanged
// 或者:始终覆盖服务器
operation.savePolicy = .changedKeys // 仅覆盖变更字段
operation.modifyRecordsResultBlock = { result in
switch result {
case .success:
print("Saved")
case .failure(let error as CKError):
if error.code == .serverRecordChanged {
// 冲突 - 手动合并
let serverRecord = error.serverRecord
let clientRecord = error.clientRecord
let merged = mergeRecords(server: serverRecord, client: clientRecord)
// 使用合并后的记录重试
}
}
}
privateDatabase.add(operation)Database Scopes
数据库作用域
| Scope | Accessibility | SwiftData Support | Use Case |
|---|---|---|---|
| Private | User only | ✅ Yes | Personal user data |
| Public | All users | ❌ No | Shared/public content |
| Shared | Invited users | ❌ No | Collaboration |
| 作用域 | 可访问性 | SwiftData支持 | 使用场景 |
|---|---|---|---|
| 私有 | 仅用户本人 | ✅ 支持 | 个人用户数据 |
| 公开 | 所有用户 | ❌ 不支持 | 共享/公开内容 |
| 共享 | 受邀用户 | ❌ 不支持 | 协作场景 |
Private Database
私有数据库
swift
// ✅ Private database (most common)
let privateDB = CKContainer.default().privateCloudDatabase
// User must be signed into iCloud
// Data syncs across user's devices
// Not visible to other usersswift
// ✅ 私有数据库(最常用)
let privateDB = CKContainer.default().privateCloudDatabase
// 用户必须登录iCloud
// 数据在用户的多设备间同步
// 对其他用户不可见Public Database
公开数据库
swift
// ✅ Public database (for shared content)
let publicDB = CKContainer.default().publicCloudDatabase
// Accessible to all app users
// Even unauthenticated users can read
// Writes require authentication
// Use for: Leaderboards, public content, discoveryswift
// ✅ 公开数据库(用于共享内容)
let publicDB = CKContainer.default().publicCloudDatabase
// 所有应用用户均可访问
// 未认证用户也可读取
// 写入需要认证
// 适用场景:排行榜、公开内容、发现页Shared Database
共享数据库
swift
// ✅ Shared database (collaboration)
let sharedDB = CKContainer.default().sharedCloudDatabase
// For CKShare-based collaboration
// Users invited to specific record zones
// Use for: Shared documents, team dataswift
// ✅ 共享数据库(协作场景)
let sharedDB = CKContainer.default().sharedCloudDatabase
// 基于CKShare的协作场景
// 用户可被邀请至特定记录区域
// 适用场景:共享文档、团队数据CloudKit Assets (Files)
CloudKit资源(文件)
swift
// ✅ Store files as CKAsset
let imageURL = saveImageToTempFile(image) // Must be file URL
let asset = CKAsset(fileURL: imageURL)
let record = CKRecord(recordType: "Photo")
record["image"] = asset
record["caption"] = "Sunset"
try await privateDatabase.save(record)
// ✅ Retrieve asset
let fetchedRecord = try await privateDatabase.record(for: recordID)
if let asset = fetchedRecord["image"] as? CKAsset,
let fileURL = asset.fileURL {
let imageData = try Data(contentsOf: fileURL)
let image = UIImage(data: imageData)
}Important: CKAsset requires a file URL, not Data. Write data to temp file first.
swift
// ✅ 将文件存储为CKAsset
let imageURL = saveImageToTempFile(image) // 必须是文件URL
let asset = CKAsset(fileURL: imageURL)
let record = CKRecord(recordType: "Photo")
record["image"] = asset
record["caption"] = "Sunset"
try await privateDatabase.save(record)
// ✅ 获取资源
let fetchedRecord = try await privateDatabase.record(for: recordID)
if let asset = fetchedRecord["image"] as? CKAsset,
let fileURL = asset.fileURL {
let imageData = try Data(contentsOf: fileURL)
let image = UIImage(data: imageData)
}重要提示:CKAsset需要文件URL,而非Data类型。请先将数据写入临时文件。
CloudKit Console (Monitoring - WWDC 2024)
CloudKit控制台(监控 - WWDC 2024)
Developer Notifications
开发者通知
Set up alerts for:
- Schema changes
- Quota exceeded
- High error rates
- Custom thresholds
设置告警以监控:
- 架构变更
- 配额超限
- 高错误率
- 自定义阈值
Telemetry
遥测数据
Monitor:
- Request count
- Error rate
- Latency (p50, p95, p99)
- Bandwidth usage
监控指标:
- 请求次数
- 错误率
- 延迟(p50、p95、p99)
- 带宽使用量
Logs
日志
View:
- Individual requests
- Error details
- Performance bottlenecks
Common Patterns
常见模式
Pattern 1: Initial Sync
模式1:初始同步
swift
// ✅ Fetch all records on first launch
func performInitialSync() async throws {
let predicate = NSPredicate(value: true) // All records
let query = CKQuery(recordType: "Task", predicate: predicate)
let (results, _) = try await privateDatabase.records(matching: query)
for result in results {
if case .success(let record) = result.1 {
saveToLocalDatabase(record)
}
}
}swift
// ✅ 首次启动时获取所有记录
func performInitialSync() async throws {
let predicate = NSPredicate(value: true) // 所有记录
let query = CKQuery(recordType: "Task", predicate: predicate)
let (results, _) = try await privateDatabase.records(matching: query)
for result in results {
if case .success(let record) = result.1 {
saveToLocalDatabase(record)
}
}
}Pattern 2: Incremental Sync
模式2:增量同步
swift
// ✅ Use CKServerChangeToken for incremental fetches
func fetchChanges(since token: CKServerChangeToken?) async throws {
let zoneID = CKRecordZone.ID(zoneName: "Tasks")
let config = CKFetchRecordZoneChangesOperation.ZoneConfiguration(
previousServerChangeToken: token
)
let operation = CKFetchRecordZoneChangesOperation(
recordZoneIDs: [zoneID],
configurationsByRecordZoneID: [zoneID: config]
)
operation.recordWasChangedBlock = { recordID, result in
if case .success(let record) = result {
updateLocalDatabase(with: record)
}
}
operation.recordWithIDWasDeletedBlock = { recordID, _ in
deleteFromLocalDatabase(recordID)
}
operation.recordZoneFetchResultBlock = { zoneID, result in
if case .success(let (token, _, _)) = result {
saveChangeToken(token) // For next fetch
}
}
try await privateDatabase.add(operation)
}swift
// ✅ 使用CKServerChangeToken实现增量获取
func fetchChanges(since token: CKServerChangeToken?) async throws {
let zoneID = CKRecordZone.ID(zoneName: "Tasks")
let config = CKFetchRecordZoneChangesOperation.ZoneConfiguration(
previousServerChangeToken: token
)
let operation = CKFetchRecordZoneChangesOperation(
recordZoneIDs: [zoneID],
configurationsByRecordZoneID: [zoneID: config]
)
operation.recordWasChangedBlock = { recordID, result in
if case .success(let record) = result {
updateLocalDatabase(with: record)
}
}
operation.recordWithIDWasDeletedBlock = { recordID, _ in
deleteFromLocalDatabase(recordID)
}
operation.recordZoneFetchResultBlock = { zoneID, result in
if case .success(let (token, _, _)) = result {
saveChangeToken(token) // 用于下次获取
}
}
try await privateDatabase.add(operation)
}Entitlements
权限配置
Required entitlements in Xcode:
xml
<!-- iCloud capability -->
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudKit</string>
</array>
<!-- CloudKit container -->
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.example.app</string>
</array>Setup:
- Xcode → Target → Signing & Capabilities
- "+ Capability" → iCloud
- Check "CloudKit"
- Select or create container
Xcode中所需的权限配置:
xml
<!-- iCloud功能权限 -->
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudKit</string>
</array>
<!-- CloudKit容器 -->
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.example.app</string>
</array>配置步骤:
- Xcode → 目标项目 → 签名与功能
- 「+ 功能」→ iCloud
- 勾选「CloudKit」
- 选择或创建容器
Quick Reference
快速参考
| Task | Modern API (iOS 17+) | Legacy API |
|---|---|---|
| Structured data sync | SwiftData + CloudKit | CKSyncEngine or CKDatabase |
| Custom persistence sync | CKSyncEngine | CKDatabase |
| Conflict resolution | Automatic (SwiftData/CKSyncEngine) | Manual (savePolicy) |
| Account changes | Handled automatically | Manual detection |
| Monitoring | CloudKit Console telemetry | Manual logging |
| 任务 | 现代API(iOS 17+) | 遗留API |
|---|---|---|
| 结构化数据同步 | SwiftData + CloudKit | CKSyncEngine或CKDatabase |
| 自定义持久化同步 | CKSyncEngine | CKDatabase |
| 冲突解决 | 自动处理(SwiftData/CKSyncEngine) | 手动处理(savePolicy) |
| 账户变更 | 自动处理 | 手动检测 |
| 监控 | CloudKit控制台遥测 | 手动日志 |
Related Skills
相关技能
- — SwiftData implementation details
axiom-swiftdata - — Choose CloudKit vs iCloud Drive
axiom-storage - — File-based iCloud sync
axiom-icloud-drive-ref - — Debug CloudKit sync issues
axiom-cloud-sync-diag
Last Updated: 2025-12-12
Skill Type: Reference
Minimum iOS: 10.0 (basic), 17.0 (CKSyncEngine, SwiftData integration)
WWDC Sessions: 2023-10188 (CKSyncEngine), 2024-10122 (CloudKit Console)
- — SwiftData实现细节
axiom-swiftdata - — CloudKit与iCloud Drive选择指南
axiom-storage - — 基于文件的iCloud同步
axiom-icloud-drive-ref - — CloudKit同步问题调试
axiom-cloud-sync-diag
最后更新:2025-12-12
技能类型:参考文档
最低iOS版本:10.0(基础功能),17.0(CKSyncEngine、SwiftData集成)
WWDC会话:2023-10188(CKSyncEngine),2024-10122(CloudKit控制台)