axiom-cloudkit-ref

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CloudKit 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
axiom-icloud-drive-ref
instead)
在以下场景使用本技能:
  • 实现结构化数据向iCloud的同步
  • 在SwiftData+CloudKit、CKSyncEngine或原生CloudKit API之间做选择
  • 配置公开/私有/共享数据库
  • 实现冲突解决逻辑
  • 调试CloudKit同步问题
  • 监控CloudKit性能
不适用场景:简单文件同步(请使用
axiom-icloud-drive-ref
技能)

Overview

概述

CloudKit is for STRUCTURED DATA sync (records with relationships), not simple file sync.
Three modern approaches:
  1. SwiftData + CloudKit (Easiest, iOS 17+)
  2. CKSyncEngine (Custom persistence, iOS 17+, WWDC 2023)
  3. Raw CloudKit APIs (Maximum control, more complexity)

CloudKit适用于结构化数据同步(带关联关系的记录),而非简单文件同步。
三种现代实现方式:
  1. SwiftData + CloudKit(最简单,iOS 17+)
  2. CKSyncEngine(自定义持久化,iOS 17+,WWDC 2023)
  3. 原生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 automatically
Entitlements required:
  • iCloud capability
  • CloudKit container
Use
axiom-swiftdata
skill for SwiftData details

适用场景:使用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-swiftdata
技能

Approach 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:
  • CKContainer
    — Entry point
  • CKDatabase
    — Public/private/shared scope
  • CKRecord
    — Individual data record
  • CKRecordZone
    — Logical grouping
  • CKAsset
    — Binary file storage
适用场景:仅当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

数据库作用域

ScopeAccessibilitySwiftData SupportUse Case
PrivateUser only✅ YesPersonal user data
PublicAll users❌ NoShared/public content
SharedInvited users❌ NoCollaboration
作用域可访问性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 users
swift
// ✅ 私有数据库(最常用)
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, discovery
swift
// ✅ 公开数据库(用于共享内容)
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 data

swift
// ✅ 共享数据库(协作场景)
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:
  1. Xcode → Target → Signing & Capabilities
  2. "+ Capability" → iCloud
  3. Check "CloudKit"
  4. 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>
配置步骤
  1. Xcode → 目标项目 → 签名与功能
  2. 「+ 功能」→ iCloud
  3. 勾选「CloudKit」
  4. 选择或创建容器

Quick Reference

快速参考

TaskModern API (iOS 17+)Legacy API
Structured data syncSwiftData + CloudKitCKSyncEngine or CKDatabase
Custom persistence syncCKSyncEngineCKDatabase
Conflict resolutionAutomatic (SwiftData/CKSyncEngine)Manual (savePolicy)
Account changesHandled automaticallyManual detection
MonitoringCloudKit Console telemetryManual logging

任务现代API(iOS 17+)遗留API
结构化数据同步SwiftData + CloudKitCKSyncEngine或CKDatabase
自定义持久化同步CKSyncEngineCKDatabase
冲突解决自动处理(SwiftData/CKSyncEngine)手动处理(savePolicy)
账户变更自动处理手动检测
监控CloudKit控制台遥测手动日志

Related Skills

相关技能

  • axiom-swiftdata
    — SwiftData implementation details
  • axiom-storage
    — Choose CloudKit vs iCloud Drive
  • axiom-icloud-drive-ref
    — File-based iCloud sync
  • axiom-cloud-sync-diag
    — Debug CloudKit sync issues

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)
  • axiom-swiftdata
    — SwiftData实现细节
  • axiom-storage
    — CloudKit与iCloud Drive选择指南
  • axiom-icloud-drive-ref
    — 基于文件的iCloud同步
  • axiom-cloud-sync-diag
    — CloudKit同步问题调试

最后更新:2025-12-12 技能类型:参考文档 最低iOS版本:10.0(基础功能),17.0(CKSyncEngine、SwiftData集成) WWDC会话:2023-10188(CKSyncEngine),2024-10122(CloudKit控制台)