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
- Container and Database Setup
- Workflow
- CKRecord CRUD
- CKQuery
- CKSubscription
- CKSyncEngine (iOS 17+)
- SwiftData + CloudKit
- NSUbiquitousKeyValueStore
- iCloud Drive File Sync
- Account Status and Error Handling
- Conflict Resolution
- Common Mistakes
- Review Checklist
- References
Workflow
- Choose the database scope and sync owner; verify capability, container, account status, schema, and environment before writing records.
- Make a local change durable, enqueue it, then let subscriptions or
CKSyncEnginedrive remote work rather than polling. - Persist change tokens or sync-engine state after successful application.
- Test offline edits, partial failure, rate limiting, token expiry, conflict, account loss, zone deletion, and relaunch.
- On failure, classify the
CKError, restore the affected fixture or queue item, apply the documented retry/reset/merge action, and rerun the same scenario. Never restart a full sync blindly after partial success.
Load references/cloudkit-patterns.md [blocked] for incremental zone changes, shares, assets, batch operations, and Dashboard procedures.
Container and Database Setup
Enable iCloud + CloudKit in Signing & Capabilities. A container provides three databases:
CKRecord CRUD
Records are key-value pairs. Max 1 MB per record (excluding CKAsset data).
Custom Record Zones
Apps create custom zones in the private database. Shared databases expose zones that other users share with the current user. Custom zones support atomic commits, change tracking, and sharing; public databases do not support custom zones.
CKQuery
Query records with NSPredicate. Supported: ==, !=, <, >, <=, >=,
BEGINSWITH, CONTAINS, IN, AND, NOT, BETWEEN,
distanceToLocation:fromLocation:.
CONTAINS tests list membership except for tokenized full-text search with
self CONTAINS. BEGINSWITH is the string-prefix operator; unsupported
operators, key paths, or field types fail when the query executes.
For every encryption review, explicitly call out field eligibility: encrypted
values cannot be queried or sorted; CKAsset is encrypted by default; and
CKRecord.Reference cannot be encrypted because CloudKit needs it server-side.
CKSubscription
Subscriptions trigger push notifications when records change server-side. CloudKit/Xcode handles the APNs entitlement when CloudKit is enabled; no separate explicit App ID push setup is needed. Silent/background processing still needs Background Modes > Remote notifications.
Handle in AppDelegate:
CKSyncEngine (iOS 17+)
CKSyncEngine is the recommended sync approach for custom model data. It
handles scheduling, transient retries, change tokens, and database
subscriptions, but not app-specific save failures: CKError.serverRecordChanged
from sentRecordZoneChanges.failedRecordSaves still requires custom conflict
resolution and rescheduling. Automatic sync timing is indeterminate. Requires
CloudKit capability + Remote notifications; private/shared databases only.
Key point: persist stateSerialization across launches; the engine needs it
to resume from the correct change token.
SwiftData + CloudKit
ModelConfiguration supports CloudKit sync. In every SwiftData CloudKit
implementation or review, always report two verdicts:
- Model compatibility: no
#Uniqueor unique constraints, optional relationships, no.deny, and external storage for largeData. - Schema rollout: initialize the development schema in nonproduction builds, verify it in CloudKit Dashboard, promote it before release, and after production promotion only add schema; don't delete model types or change existing attributes.
NSUbiquitousKeyValueStore
Simple key-value sync. Max 1024 keys, 1 MB total, 1 MB per value. Stores locally when iCloud is unavailable.
iCloud Drive File Sync
Use FileManager ubiquity APIs for document-level sync. Call
url(forUbiquityContainerIdentifier:) and setUbiquitous off the main thread;
setUbiquitous performs coordinated file work and can block. If the app is
presenting the file, configure an active file presenter before moving it.
Monitor files with NSMetadataQuery scoped to
NSMetadataQueryUbiquitousDocumentsScope or
NSMetadataQueryUbiquitousDataScope.
Account Status and Error Handling
Always check account status before sync. Listen for .CKAccountChanged.
CKError Handling
Conflict Resolution
When saving a record that changed server-side, CloudKit returns
.serverRecordChanged with three record versions. Always merge into
serverRecord -- it has the correct change tag.
Common Mistakes
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 [blocked] 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


