cloudkit
Implement, review, or improve CloudKit and iCloud sync in iOS/macOS apps. Use when working with CKContainer, CKRecord, CKQuery, CKSubscription, CKSyncEngine, CKShare, NSUbiquitousKeyValueStore, or iCloud Drive file coordination; when syncing SwiftData models via ModelConfiguration with cloudKitDatab
By dpearson2699 · 2,804 installs
npx skills add dpearson2699/swift-ios-skills --skill cloudkit
Source repository · Upstream listing
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]( container and database setup)
[Workflow]( workflow)
[CKRecord CRUD]( ckrecord crud)
[CKQuery]( ckquery)
[CKSubscription]( cksubscription)
[CKSyncEngine (iOS 17+)]( cksyncengine ios 17)
[SwiftData + CloudKit]( swiftdata cloudkit)
[NSUbiquitousKeyValueStore]( nsubiquitouskeyvaluestore)
[iCloud Drive File Sync]( icloud drive file sync)
[Account Status and Error Handling]( account status and error handling)
[Conflict Resolution]( conflict resolution)
[Common Mistakes]( common mistakes)
[Review Checklist]( review checklist)
[References]( references)
Workflow
1. Choose the database scope and sync owner; verify capability, container, account status, schema, and environment before writing records.
2. Make a local change durable, enqueue it, then let subscriptions or CKSyncEngine drive remote work rather than polling.
3. Persist change tokens or sync engine state after successful application.
4. Test offline edits, partial failure, rate limiting, token expiry, conflict, account loss, zone deletion, and relaunch.
5. 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](references/cloudkit patterns.md) 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:
Database Scope Requires iCloud Storage Quota
Public All users Read: No, Write: Yes App quota
Private Current user Yes User quota
Shared Shared records Yes Owner quota
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 Unique or unique constraints, optional
relationships, no .deny , and external storage for large Data .
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
Error Code Strategy
.networkFailure , .networkUnavailable Queue for retry when network returns
.serverRecordChanged Three way merge (see Conflict Resolution)
.requestRateLimited , .zoneBusy , .serviceUnavailable Retry after retryAfterSeconds
.quotaExceeded Notify user; reduce data usage
.notAuthenticated Prompt iCloud sign in
.partialFailure Inspect partialErrorsByItemID per item
.changeTokenExpired Reset token, refetch all changes
.userDeletedZone Recreate zone and re upload data
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
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; .noAccount handled 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.serverRecordChanged handled with three way merge into serverRecord
[ ] Network failures queued for retry; retryAfterSeconds respected
[ ] CKDatabaseSubscription or CKSyncEngine used for push based sync; Remote notifications enabled for background delivery
[ ] Change tokens persisted to disk; changeTokenExpired resets and refetches
[ ] .partialFailure errors inspected per item via partialErrorsByItemID
[ ] .userDeletedZone handled 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.didChangeExternallyNotification observed
[ ] Encryption review says CKRecord.Reference cannot use encryptedValues because CloudKit needs it server side; no query/sort on encrypted fields; CKAsset is encrypted by default
[ ] CKSyncEngine state serialization persisted across launches (iOS 17+)
References
See [references/cloudkit patterns.md](references/cloudkit patterns.md) for incremental sync, CKShare, zones, CKAsset storage, batch operations, and Dashboard usage.
[CloudKit Framework](https://sosumi.ai/documentation/cloudkit)
[CKContainer](https://sosumi.ai/documentation/cloudkit/ckcontainer)
[CKRecord](https://sosumi.ai/documentation/cloudkit/ckrecord)
[CKQuery](https://sosumi.ai/documentation/cloudkit/ckquery)
[CKSubscription](https://sosumi.ai/documentation/cloudkit/cksubscription)
[CKSyncEngine](https://sosumi.ai/documentation/cloudkit/cksyncengine)
[CKShare](https://sosumi.ai/documentation/cloudkit/ckshare)
[CKError](https://sosumi.ai/documentation/cloudkit/ckerror)
[NSUbiquitousKeyValueStore](https://sosumi.ai/documentation/foundation/nsubiquitouskeyvaluestore)
[SwiftData CloudKit sync](https://sosumi.ai/documentation/swiftdata/syncing model data across a persons devices)