CloudKit
Sync data across devices using CloudKit, iCloud key-value storage, and iCloud Drive. Covers container setup, record CRUD, queries, subscriptions, CKSyncEngine, SwiftData integration, conflict resolution, and error handling.
Contents
Workflow
- Choose container, public/private/shared database, record ownership, zone strategy, and offline expectations.
- Define stable record types, identifiers, references, assets, and conflict fields before writing CRUD code.
- Use queries for bounded reads and
CKSyncEngineor change tokens for durable incremental synchronization. - Handle account changes, partial failures, rate limits, retries, conflicts, deletions, and local persistence explicitly.
- Verify offline edits, concurrent devices, account switching, quota, schema deployment, sharing, and production environment behavior.
Route by Task
- Read core implementation details for containers, records, queries, subscriptions,
CKSyncEngine, SwiftData, key-value storage, iCloud Drive, and error handling. - Read extended CloudKit patterns for change tokens, shares, assets, batch operations, custom zones, and Dashboard procedures.
Core Decisions
- Treat record saves as conflict-prone distributed writes, not local CRUD.
- Persist sync state/change tokens and recover from token expiration.
- Inspect per-item errors in partial failures and honor retry guidance.
- Keep SwiftData CloudKit configuration constraints separate from direct CloudKit workflows.
Common Mistakes
| Mistake | Fix |
|---|---|
| Syncing without an account gate | Check accountStatus() and model .noAccount as a user-visible state. |
| Personal data in the public database | Use private scope for user data; public scope is app-wide content. |
| Timer polling | Use database subscriptions or CKSyncEngine. |
| Immediate retry after throttling | Respect retryAfterSeconds and preserve pending work. |
| Assuming the engine resolves conflicts | Three-way merge failedRecordSaves, then reschedule the save. |
| Starting every fetch with a nil token | Persist tokens/state; reset only on the documented expiry path. |
Review Checklist
- iCloud + CloudKit capability enabled in Signing & Capabilities
- Account status checked before sync;
.noAccounthandled gracefully - Private database used for user data; public only for shared content
- Custom record zones created in private DB; shared DB zones discovered from shares
-
CKError.serverRecordChangedhandled with three-way merge intoserverRecord - Network failures queued for retry;
retryAfterSecondsrespected -
CKDatabaseSubscriptionorCKSyncEngineused for push-based sync; Remote notifications enabled for background delivery - Change tokens persisted to disk;
changeTokenExpiredresets and refetches -
.partialFailureerrors inspected per-item viapartialErrorsByItemID -
.userDeletedZonehandled by recreating zone and resyncing - SwiftData CloudKit review reports model compatibility and schema rollout: initialized/verified development schema, promoted before release, and additive-only production changes
-
NSUbiquitousKeyValueStore.didChangeExternallyNotificationobserved - Encryption review says
CKRecord.Referencecannot useencryptedValuesbecause CloudKit needs it server-side; no query/sort on encrypted fields;CKAssetis encrypted by default -
CKSyncEnginestate serialization persisted across launches (iOS 17+)
References
- See references/cloudkit-patterns.md for incremental sync, CKShare, zones, CKAsset storage, batch operations, and Dashboard usage.
- CloudKit Framework
- CKContainer
- CKRecord
- CKQuery
- CKSubscription
- CKSyncEngine
- CKShare
- CKError
- NSUbiquitousKeyValueStore
- SwiftData CloudKit sync
- Core implementation details -- setup, API wiring, and focused implementation recipes moved out of the entrypoint.