{"id":"ca93e98d-550b-46a2-8452-c8dce326b65b","shortId":"ZFQSa5","kind":"skill","title":"cloudkit","tagline":"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 ModelConfiguratio","description":"# CloudKit\n\nSync data across devices using CloudKit, iCloud key-value storage, and iCloud\nDrive. Covers container setup, record CRUD, queries, subscriptions, CKSyncEngine,\nSwiftData integration, conflict resolution, and error handling. Targets iOS 26+\nwith Swift 6.3; older availability noted where relevant.\n\n## Contents\n\n- [Container and Database Setup](#container-and-database-setup)\n- [CKRecord CRUD](#ckrecord-crud)\n- [CKQuery](#ckquery)\n- [CKSubscription](#cksubscription)\n- [CKSyncEngine (iOS 17+)](#cksyncengine-ios-17)\n- [SwiftData + CloudKit](#swiftdata--cloudkit)\n- [NSUbiquitousKeyValueStore](#nsubiquitouskeyvaluestore)\n- [iCloud Drive File Sync](#icloud-drive-file-sync)\n- [Account Status and Error Handling](#account-status-and-error-handling)\n- [Conflict Resolution](#conflict-resolution)\n- [Common Mistakes](#common-mistakes)\n- [Review Checklist](#review-checklist)\n- [References](#references)\n\n## Container and Database Setup\n\nEnable iCloud + CloudKit in Signing & Capabilities. A container provides\nthree databases:\n\n| Database | Scope | Requires iCloud | Storage Quota |\n|----------|-------|-----------------|---------------|\n| Public   | All users | Read: No, Write: Yes | App quota |\n| Private  | Current user | Yes | User quota |\n| Shared   | Shared records | Yes | Owner quota |\n\n```swift\nimport CloudKit\n\nlet container = CKContainer.default()\n// Or named: CKContainer(identifier: \"iCloud.com.example.app\")\n\nlet publicDB  = container.publicCloudDatabase\nlet privateDB = container.privateCloudDatabase\nlet sharedDB  = container.sharedCloudDatabase\n```\n\n## CKRecord CRUD\n\nRecords are key-value pairs. Max 1 MB per record (excluding CKAsset data).\n\n```swift\n// CREATE\nlet record = CKRecord(recordType: \"Note\")\nrecord[\"title\"] = \"Meeting Notes\" as CKRecordValue\nrecord[\"body\"] = \"Discussed Q3 roadmap\" as CKRecordValue\nrecord[\"createdAt\"] = Date() as CKRecordValue\nrecord[\"tags\"] = [\"work\", \"planning\"] as CKRecordValue\nlet saved = try await privateDB.save(record)\n\n// FETCH by ID\nlet recordID = CKRecord.ID(recordName: \"unique-id-123\")\nlet fetched = try await privateDB.record(for: recordID)\n\n// UPDATE -- fetch first, modify, then save\nfetched[\"title\"] = \"Updated Title\" as CKRecordValue\nlet updated = try await privateDB.save(fetched)\n\n// DELETE\ntry await privateDB.deleteRecord(withID: recordID)\n```\n\n### Custom Record Zones (Private/Shared Only)\n\nCustom zones support atomic commits, change tracking, and sharing.\n\n```swift\nlet zoneID = CKRecordZone.ID(zoneName: \"NotesZone\")\nlet zone = CKRecordZone(zoneID: zoneID)\ntry await privateDB.save(zone)\n\nlet recordID = CKRecord.ID(recordName: UUID().uuidString, zoneID: zoneID)\nlet record = CKRecord(recordType: \"Note\", recordID: recordID)\n```\n\n## CKQuery\n\nQuery records with NSPredicate. Supported: `==`, `!=`, `<`, `>`, `<=`, `>=`,\n`BEGINSWITH`, `CONTAINS`, `IN`, `AND`, `NOT`, `BETWEEN`,\n`distanceToLocation:fromLocation:`.\n\n```swift\nlet predicate = NSPredicate(format: \"title BEGINSWITH %@\", \"Meeting\")\nlet query = CKQuery(recordType: \"Note\", predicate: predicate)\nquery.sortDescriptors = [NSSortDescriptor(key: \"createdAt\", ascending: false)]\n\nlet (results, _) = try await privateDB.records(matching: query)\nfor (_, result) in results {\n    let record = try result.get()\n    print(record[\"title\"] as? String ?? \"\")\n}\n\n// Fetch all records of a type\nlet allQuery = CKQuery(recordType: \"Note\", predicate: NSPredicate(value: true))\n\n// Full-text search across string fields\nlet searchQuery = CKQuery(\n    recordType: \"Note\",\n    predicate: NSPredicate(format: \"self CONTAINS %@\", \"roadmap\")\n)\n\n// Compound predicate\nlet compound = NSCompoundPredicate(andPredicateWithSubpredicates: [\n    NSPredicate(format: \"createdAt > %@\", cutoffDate as NSDate),\n    NSPredicate(format: \"tags CONTAINS %@\", \"work\")\n])\n```\n\n## CKSubscription\n\nSubscriptions trigger push notifications when records change server-side.\nCloudKit auto-enables APNs -- no explicit push entitlement needed.\n\n```swift\n// Query subscription -- fires when matching records change\nlet subscription = CKQuerySubscription(\n    recordType: \"Note\",\n    predicate: NSPredicate(format: \"tags CONTAINS %@\", \"urgent\"),\n    subscriptionID: \"urgent-notes\",\n    options: [.firesOnRecordCreation, .firesOnRecordUpdate]\n)\nlet notifInfo = CKSubscription.NotificationInfo()\nnotifInfo.shouldSendContentAvailable = true  // silent push\nsubscription.notificationInfo = notifInfo\ntry await privateDB.save(subscription)\n\n// Database subscription -- fires on any database change\nlet dbSub = CKDatabaseSubscription(subscriptionID: \"private-db-changes\")\ndbSub.notificationInfo = notifInfo\ntry await privateDB.save(dbSub)\n\n// Record zone subscription -- fires on changes within a zone\nlet zoneSub = CKRecordZoneSubscription(\n    zoneID: CKRecordZone.ID(zoneName: \"NotesZone\"),\n    subscriptionID: \"notes-zone-changes\"\n)\nzoneSub.notificationInfo = notifInfo\ntry await privateDB.save(zoneSub)\n```\n\nHandle in AppDelegate:\n\n```swift\nfunc application(\n    _ application: UIApplication,\n    didReceiveRemoteNotification userInfo: [AnyHashable: Any]\n) async -> UIBackgroundFetchResult {\n    let notification = CKNotification(fromRemoteNotificationDictionary: userInfo)\n    guard notification?.subscriptionID == \"private-db-changes\" else { return .noData }\n    // Fetch changes using CKSyncEngine or CKFetchRecordZoneChangesOperation\n    return .newData\n}\n```\n\n## CKSyncEngine (iOS 17+)\n\n`CKSyncEngine` is the recommended sync approach. It handles scheduling,\ntransient error retries, change tokens, and push notifications automatically.\nWorks with private and shared databases only.\n\n```swift\nimport CloudKit\n\nfinal class SyncManager: CKSyncEngineDelegate {\n    let syncEngine: CKSyncEngine\n\n    init(container: CKContainer = .default()) {\n        let config = CKSyncEngine.Configuration(\n            database: container.privateCloudDatabase,\n            stateSerialization: Self.loadState(),\n            delegate: self\n        )\n        self.syncEngine = CKSyncEngine(config)\n    }\n\n    func handleEvent(_ event: CKSyncEngine.Event, syncEngine: CKSyncEngine) {\n        switch event {\n        case .stateUpdate(let update):\n            Self.saveState(update.stateSerialization)\n        case .accountChange(let change):\n            handleAccountChange(change)\n        case .fetchedRecordZoneChanges(let changes):\n            for mod in changes.modifications { processRemoteRecord(mod.record) }\n            for del in changes.deletions { processRemoteDeletion(del.recordID) }\n        case .sentRecordZoneChanges(let sent):\n            for saved in sent.savedRecords { markSynced(saved) }\n            for fail in sent.failedRecordSaves { handleSaveFailure(fail) }\n        default: break\n        }\n    }\n\n    func nextRecordZoneChangeBatch(\n        _ context: CKSyncEngine.SendChangesContext,\n        syncEngine: CKSyncEngine\n    ) -> CKSyncEngine.RecordZoneChangeBatch? {\n        let pending = syncEngine.state.pendingRecordZoneChanges\n        return CKSyncEngine.RecordZoneChangeBatch(\n            pendingChanges: Array(pending)\n        ) { recordID in self.recordToSend(for: recordID) }\n    }\n}\n\n// Schedule changes\nlet zoneID = CKRecordZone.ID(zoneName: \"NotesZone\")\nlet recordID = CKRecord.ID(recordName: noteID, zoneID: zoneID)\nsyncEngine.state.add(pendingRecordZoneChanges: [.saveRecord(recordID)])\n\n// Trigger immediate sync (pull-to-refresh)\ntry await syncEngine.fetchChanges()\ntry await syncEngine.sendChanges()\n```\n\n**Key point**: persist `stateSerialization` across launches; the engine needs it\nto resume from the correct change token.\n\n## SwiftData + CloudKit\n\n`ModelConfiguration` supports CloudKit sync. CloudKit models must use optional\nproperties and avoid unique constraints.\n\n```swift\nimport SwiftData\n\n@Model\nclass Note {\n    var title: String\n    var body: String?\n    var createdAt: Date?\n    @Attribute(.externalStorage) var imageData: Data?\n\n    init(title: String, body: String? = nil) {\n        self.title = title\n        self.body = body\n        self.createdAt = Date()\n    }\n}\n\nlet config = ModelConfiguration(\n    \"Notes\",\n    cloudKitDatabase: .private(\"iCloud.com.example.app\")\n)\nlet container = try ModelContainer(for: Note.self, configurations: config)\n```\n\n**CloudKit model rules**: all relationships must be optional; avoid\n`#Unique` (unique constraints are unsupported); keep models flat; use\n`@Attribute(.externalStorage)` for large data; avoid complex relationship graphs.\n\n## NSUbiquitousKeyValueStore\n\nSimple key-value sync. Max 1024 keys, 1 MB total, 1 MB per value. Stores\nlocally when iCloud is unavailable.\n\n```swift\nlet kvStore = NSUbiquitousKeyValueStore.default\n\n// Write\nkvStore.set(\"dark\", forKey: \"theme\")\nkvStore.set(14.0, forKey: \"fontSize\")\nkvStore.set(true, forKey: \"notificationsEnabled\")\nkvStore.synchronize()\n\n// Read\nlet theme = kvStore.string(forKey: \"theme\") ?? \"system\"\n\n// Observe external changes\nNotificationCenter.default.addObserver(\n    forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification,\n    object: kvStore, queue: .main\n) { notification in\n    guard let userInfo = notification.userInfo,\n          let reason = userInfo[NSUbiquitousKeyValueStoreChangeReasonKey] as? Int,\n          let keys = userInfo[NSUbiquitousKeyValueStoreChangedKeysKey] as? [String]\n    else { return }\n\n    switch reason {\n    case NSUbiquitousKeyValueStoreServerChange:\n        for key in keys { applyRemoteChange(key: key) }\n    case NSUbiquitousKeyValueStoreInitialSyncChange:\n        reloadAllSettings()\n    case NSUbiquitousKeyValueStoreQuotaViolationChange:\n        handleQuotaExceeded()\n    default: break\n    }\n}\n```\n\n## iCloud Drive File Sync\n\nUse `FileManager` ubiquity APIs for document-level sync.\n\n```swift\nguard let ubiquityURL = FileManager.default.url(\n    forUbiquityContainerIdentifier: \"iCloud.com.example.app\"\n) else { return }  // iCloud not available\n\nlet docsURL = ubiquityURL.appendingPathComponent(\"Documents\")\nlet cloudURL = docsURL.appendingPathComponent(\"report.pdf\")\ntry FileManager.default.setUbiquitous(true, itemAt: localURL, destinationURL: cloudURL)\n\n// Monitor iCloud files\nlet query = NSMetadataQuery()\nquery.predicate = NSPredicate(format: \"%K LIKE '*.pdf'\", NSMetadataItemFSNameKey)\nquery.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope]\nNotificationCenter.default.addObserver(\n    forName: .NSMetadataQueryDidFinishGathering, object: query, queue: .main\n) { _ in\n    query.disableUpdates()\n    for item in query.results as? [NSMetadataItem] ?? [] {\n        let name = item.value(forAttribute: NSMetadataItemFSNameKey) as? String\n        let status = item.value(\n            forAttribute: NSMetadataUbiquitousItemDownloadingStatusKey) as? String\n    }\n    query.enableUpdates()\n}\nquery.start()\n```\n\n## Account Status and Error Handling\n\nAlways check account status before sync. Listen for `.CKAccountChanged`.\n\n```swift\nfunc checkiCloudStatus() async throws -> CKAccountStatus {\n    let status = try await CKContainer.default().accountStatus()\n    switch status {\n    case .available: return status\n    case .noAccount: throw SyncError.noiCloudAccount\n    case .restricted: throw SyncError.restricted\n    case .temporarilyUnavailable: throw SyncError.temporarilyUnavailable\n    case .couldNotDetermine: throw SyncError.unknown\n    @unknown default: throw SyncError.unknown\n    }\n}\n```\n\n### CKError Handling\n\n| Error Code | Strategy |\n|-----------|----------|\n| `.networkFailure`, `.networkUnavailable` | Queue for retry when network returns |\n| `.serverRecordChanged` | Three-way merge (see Conflict Resolution) |\n| `.requestRateLimited`, `.zoneBusy`, `.serviceUnavailable` | Retry after `retryAfterSeconds` |\n| `.quotaExceeded` | Notify user; reduce data usage |\n| `.notAuthenticated` | Prompt iCloud sign-in |\n| `.partialFailure` | Inspect `partialErrorsByItemID` per item |\n| `.changeTokenExpired` | Reset token, refetch all changes |\n| `.userDeletedZone` | Recreate zone and re-upload data |\n\n```swift\nfunc handleCloudKitError(_ error: Error) {\n    guard let ckError = error as? CKError else { return }\n    switch ckError.code {\n    case .networkFailure, .networkUnavailable:\n        scheduleRetryWhenOnline()\n    case .serverRecordChanged:\n        resolveConflict(ckError)\n    case .requestRateLimited, .zoneBusy, .serviceUnavailable:\n        let delay = ckError.retryAfterSeconds ?? 3.0\n        scheduleRetry(after: delay)\n    case .quotaExceeded:\n        notifyUserStorageFull()\n    case .partialFailure:\n        if let partial = ckError.partialErrorsByItemID {\n            for (_, itemError) in partial { handleCloudKitError(itemError) }\n        }\n    case .changeTokenExpired:\n        resetChangeToken()\n    case .userDeletedZone:\n        recreateZoneAndResync()\n    default: logError(ckError)\n    }\n}\n```\n\n## Conflict Resolution\n\nWhen saving a record that changed server-side, CloudKit returns\n`.serverRecordChanged` with three record versions. Always merge into\n`serverRecord` -- it has the correct change tag.\n\n```swift\nfunc resolveConflict(_ error: CKError) {\n    guard error.code == .serverRecordChanged,\n          let ancestor = error.ancestorRecord,\n          let client = error.clientRecord,\n          let server = error.serverRecord\n    else { return }\n\n    // Merge client changes into server record\n    for key in client.changedKeys() {\n        if server[key] == ancestor[key] {\n            server[key] = client[key]           // Server unchanged, use client\n        } else if client[key] == ancestor[key] {\n            // Client unchanged, keep server (already there)\n        } else {\n            server[key] = mergeValues(          // Both changed, custom merge\n                ancestor: ancestor[key], client: client[key], server: server[key])\n        }\n    }\n\n    Task { try await CKContainer.default().privateCloudDatabase.save(server) }\n}\n```\n\n## Common Mistakes\n\n**DON'T:** Perform sync operations without checking account status.\n**DO:** Check `CKContainer.accountStatus()` first; handle `.noAccount`.\n```swift\n// WRONG\ntry await privateDB.save(record)\n// CORRECT\nguard try await CKContainer.default().accountStatus() == .available\nelse { throw SyncError.noiCloudAccount }\ntry await privateDB.save(record)\n```\n\n**DON'T:** Ignore `.serverRecordChanged` errors.\n**DO:** Implement three-way merge using ancestor, client, and server records.\n\n**DON'T:** Store user-specific data in the public database.\n**DO:** Use private database for personal data; public only for app-wide content.\n\n**DON'T:** Assume data is available immediately after save.\n**DO:** Update local cache optimistically and reconcile on fetch.\n\n**DON'T:** Poll for changes on a timer.\n**DO:** Use `CKDatabaseSubscription` or `CKSyncEngine` for push-based sync.\n```swift\n// WRONG\nTimer.scheduledTimer(withTimeInterval: 30, repeats: true) { _ in fetchAll() }\n// CORRECT\nlet sub = CKDatabaseSubscription(subscriptionID: \"db-changes\")\nsub.notificationInfo = CKSubscription.NotificationInfo()\nsub.notificationInfo?.shouldSendContentAvailable = true\ntry await privateDB.save(sub)\n```\n\n**DON'T:** Retry immediately on rate limiting.\n**DO:** Use `CKError.retryAfterSeconds` to wait the required duration.\n\n**DON'T:** Merge conflict changes into `clientRecord`.\n**DO:** Always merge into `serverRecord` -- it has the correct change tag.\n\n**DON'T:** Pass nil change token on every fetch.\n**DO:** Persist change tokens to disk and supply them on subsequent fetches.\n\n## Review Checklist\n\n- [ ] iCloud + CloudKit capability enabled in Signing & Capabilities\n- [ ] Account status checked before sync; `.noAccount` handled gracefully\n- [ ] Private database used for user data; public only for shared content\n- [ ] `CKError.serverRecordChanged` handled with three-way merge into `serverRecord`\n- [ ] Network failures queued for retry; `retryAfterSeconds` respected\n- [ ] `CKDatabaseSubscription` or `CKSyncEngine` used for push-based sync\n- [ ] Change tokens persisted to disk; `changeTokenExpired` resets and refetches\n- [ ] `.partialFailure` errors inspected per-item via `partialErrorsByItemID`\n- [ ] `.userDeletedZone` handled by recreating zone and resyncing\n- [ ] SwiftData CloudKit models use optionals, no `#Unique`, `.externalStorage` for large data\n- [ ] `NSUbiquitousKeyValueStore.didChangeExternallyNotification` observed\n- [ ] Sensitive data uses `encryptedValues` on CKRecord (not plain fields)\n- [ ] `CKSyncEngine` state serialization persisted across launches (iOS 17+)\n\n## References\n\n- See [references/cloudkit-patterns.md](references/cloudkit-patterns.md) for CKFetchRecordZoneChangesOperation\n  incremental sync, CKShare collaboration, record zone management, CKAsset\n  file storage, batch operations, and CloudKit Dashboard usage.\n- [CloudKit Framework](https://sosumi.ai/documentation/cloudkit)\n- [CKContainer](https://sosumi.ai/documentation/cloudkit/ckcontainer)\n- [CKRecord](https://sosumi.ai/documentation/cloudkit/ckrecord)\n- [CKQuery](https://sosumi.ai/documentation/cloudkit/ckquery)\n- [CKSubscription](https://sosumi.ai/documentation/cloudkit/cksubscription)\n- [CKSyncEngine](https://sosumi.ai/documentation/cloudkit/cksyncengine)\n- [CKShare](https://sosumi.ai/documentation/cloudkit/ckshare)\n- [CKError](https://sosumi.ai/documentation/cloudkit/ckerror)\n- [NSUbiquitousKeyValueStore](https://sosumi.ai/documentation/foundation/nsubiquitouskeyvaluestore)\n- [CKAsset](https://sosumi.ai/documentation/cloudkit/ckasset)","tags":["cloudkit","swift","ios","skills","dpearson2699","accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills"],"capabilities":["skill","source-dpearson2699","skill-cloudkit","topic-accessibility","topic-agent-skills","topic-ai-coding","topic-apple","topic-claude-code","topic-codex-skills","topic-cursor-skills","topic-ios","topic-ios-development","topic-liquid-glass","topic-localization","topic-mapkit"],"categories":["swift-ios-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dpearson2699/swift-ios-skills/cloudkit","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add dpearson2699/swift-ios-skills","source_repo":"https://github.com/dpearson2699/swift-ios-skills","install_from":"skills.sh"}},"qualityScore":"0.684","qualityRationale":"deterministic score 0.68 from registry signals: · indexed on github topic:agent-skills · 468 github stars · SKILL.md body (17,323 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T00:53:42.072Z","embedding":null,"createdAt":"2026-04-18T22:00:50.224Z","updatedAt":"2026-04-22T00:53:42.072Z","lastSeenAt":"2026-04-22T00:53:42.072Z","tsv":"'/documentation/cloudkit)':1645 '/documentation/cloudkit/ckasset)':1681 '/documentation/cloudkit/ckcontainer)':1649 '/documentation/cloudkit/ckerror)':1673 '/documentation/cloudkit/ckquery)':1657 '/documentation/cloudkit/ckrecord)':1653 '/documentation/cloudkit/ckshare)':1669 '/documentation/cloudkit/cksubscription)':1661 '/documentation/cloudkit/cksyncengine)':1665 '/documentation/foundation/nsubiquitouskeyvaluestore)':1677 '1':216,871,874 '1024':869 '123':270 '14.0':894 '17':97,101,598,1618 '26':67 '3.0':1184 '30':1436 '6.3':70 'account':117,123,1044,1051,1326,1521 'account-status-and-error-handl':122 'accountchang':665 'accountstatus':1069,1345 'across':38,420,759,1615 'allqueri':408 'alreadi':1292 'alway':1049,1230,1481 'ancestor':1249,1272,1286,1302,1303,1366 'andpredicatewithsubpred':439 'anyhash':569 'api':965 'apn':466 'app':12,173,1393 'app-wid':1392 'appdeleg':561 'applic':564,565 'applyremotechang':947 'approach':604 'array':717 'ascend':379 'assum':1398 'async':571,1061 'atom':310 'attribut':803,853 'auto':464 'auto-en':463 'automat':616 'avail':72,982,1073,1346,1401 'avoid':785,843,858 'await':257,274,293,298,328,384,508,529,556,750,753,1067,1313,1337,1343,1351,1455 'base':1430,1563 'batch':1635 'beginswith':352,366 'bodi':237,798,811,817 'break':703,957 'cach':1408 'capabl':154,1516,1520 'case':658,664,670,686,941,950,953,1072,1076,1080,1084,1088,1169,1173,1177,1188,1191,1203,1206 'chang':312,458,479,517,525,537,552,584,589,611,667,669,673,725,770,911,1145,1219,1238,1261,1299,1418,1448,1477,1489,1495,1502,1565 'changes.deletions':683 'changes.modifications':677 'changetokenexpir':1140,1204,1570 'check':1050,1325,1329,1523 'checkicloudstatus':1060 'checklist':139,142,1513 'ckaccountchang':1057 'ckaccountstatus':1063 'ckasset':221,1632,1678 'ckcontain':17,195,636,1646 'ckcontainer.accountstatus':1330 'ckcontainer.default':192,1068,1314,1344 'ckdatabasesubscript':520,1424,1444,1556 'ckerror':1096,1161,1164,1176,1211,1244,1670 'ckerror.code':1168 'ckerror.partialerrorsbyitemid':1196 'ckerror.retryafterseconds':1183,1467 'ckerror.serverrecordchanged':1540 'ckfetchrecordzonechangesoper':593,1624 'cknotif':575 'ckqueri':19,91,92,346,370,409,425,1654 'ckquerysubscript':482 'ckrecord':18,86,89,207,227,341,1607,1650 'ckrecord-crud':88 'ckrecord.id':265,333,733 'ckrecordvalu':235,242,247,253,289 'ckrecordzon':324 'ckrecordzone.id':319,545,728 'ckrecordzonesubscript':543 'ckshare':22,1627,1666 'cksubscript':20,93,94,451,1658 'cksubscription.notificationinfo':500,1450 'cksyncengin':21,57,95,99,591,596,599,633,648,655,709,1426,1558,1611,1662 'cksyncengine-io':98 'cksyncengine.configuration':640 'cksyncengine.event':653 'cksyncengine.recordzonechangebatch':710,715 'cksyncengine.sendchangescontext':707 'cksyncenginedeleg':630 'class':628,792 'client':1252,1260,1276,1281,1284,1288,1305,1306,1367 'client.changedkeys':1268 'clientrecord':1479 'cloudkit':1,6,35,41,103,105,151,189,462,626,773,776,778,835,1223,1515,1590,1638,1641 'cloudkitdatabas':824 'cloudurl':988,997 'code':1099 'collabor':1628 'commit':311 'common':133,136,1317 'common-mistak':135 'complex':859 'compound':434,437 'config':639,649,821,834 'configur':833 'conflict':60,128,131,1115,1212,1476 'conflict-resolut':130 'constraint':787,846 'contain':51,77,82,145,156,191,353,432,449,489,635,828 'container-and-database-setup':81 'container.privateclouddatabase':203,642 'container.publicclouddatabase':200 'container.sharedclouddatabase':206 'content':76,1395,1539 'context':706 'coordin':28 'correct':769,1237,1340,1441,1488 'couldnotdetermin':1089 'cover':50 'creat':224 'createdat':244,378,442,801 'crud':54,87,90,208 'current':176 'custom':302,307,1300 'cutoffd':443 'dark':890 'dashboard':1639 'data':37,222,807,857,1127,1153,1377,1388,1399,1534,1599,1603 'databas':79,84,147,159,160,511,516,622,641,1381,1385,1530 'date':245,802,819 'db':524,583,1447 'db-chang':1446 'dbsub':519,531 'dbsub.notificationinfo':526 'default':637,702,956,1093,1209 'del':681 'del.recordid':685 'delay':1182,1187 'deleg':645 'delet':296 'destinationurl':996 'devic':39 'didreceiveremotenotif':567 'discuss':238 'disk':1505,1569 'distancetoloc':358 'docsurl':984 'docsurl.appendingpathcomponent':989 'document':968,986 'document-level':967 'drive':26,49,109,114,959 'durat':1472 'els':585,937,978,1165,1257,1282,1294,1347 'enabl':149,465,1517 'encryptedvalu':1605 'engin':762 'entitl':470 'error':63,120,126,609,1047,1098,1157,1158,1162,1243,1358,1575 'error.ancestorrecord':1250 'error.clientrecord':1253 'error.code':1246 'error.serverrecord':1256 'event':652,657 'everi':1498 'exclud':220 'explicit':468 'extern':910 'externalstorag':804,854,1596 'fail':697,701 'failur':1550 'fals':380 'fetch':260,272,279,284,295,401,588,1413,1499,1511 'fetchal':1440 'fetchedrecordzonechang':671 'field':422,1610 'file':27,110,115,960,1000,1633 'filemanag':963 'filemanager.default.setubiquitous':992 'filemanager.default.url':975 'final':627 'fire':475,513,535 'firesonrecordcr':496 'firesonrecordupd':497 'first':280,1331 'flat':851 'fontsiz':896 'forattribut':1031,1038 'forkey':891,895,899,906 'format':364,430,441,447,487,1006 'fornam':913,1014 'forubiquitycontaineridentifi':976 'framework':1642 'fromloc':359 'fromremotenotificationdictionari':576 'full':417 'full-text':416 'func':563,650,704,1059,1155,1241 'grace':1528 'graph':861 'guard':578,921,972,1159,1245,1341 'handl':64,121,127,559,606,1048,1097,1332,1527,1541,1583 'handleaccountchang':668 'handlecloudkiterror':1156,1201 'handleev':651 'handlequotaexceed':955 'handlesavefailur':700 'icloud':8,25,42,48,108,113,150,163,881,958,980,999,1131,1514 'icloud-drive-file-sync':112 'icloud.com.example.app':197,826,977 'id':262,269 'identifi':196 'ignor':1356 'imagedata':806 'immedi':743,1402,1461 'implement':2,1360 'import':188,625,789 'improv':5 'increment':1625 'init':634,808 'inspect':1136,1576 'int':930 'integr':59 'io':66,96,100,597,1617 'ios/macos':11 'item':1023,1139,1579 'item.value':1030,1037 'itemat':994 'itemerror':1198,1202 'k':1007 'keep':849,1290 'key':44,212,377,755,865,870,932,944,946,948,949,1266,1271,1273,1275,1277,1285,1287,1296,1304,1307,1310 'key-valu':43,211,864 'kvstore':886,916 'kvstore.set':889,893,897 'kvstore.string':905 'kvstore.synchronize':901 'larg':856,1598 'launch':760,1616 'let':190,198,201,204,225,254,263,271,290,317,322,331,339,361,368,381,392,407,423,436,480,498,518,541,573,631,638,660,666,672,688,711,726,731,820,827,885,903,922,925,931,973,983,987,1001,1028,1035,1064,1160,1181,1194,1248,1251,1254,1442 'level':969 'like':1008 'limit':1464 'listen':1055 'local':879,1407 'localurl':995 'logerror':1210 'main':918,1019 'manag':1631 'marksync':694 'match':386,477 'max':215,868 'mb':217,872,875 'meet':232,367 'merg':1113,1231,1259,1301,1364,1475,1482,1546 'mergevalu':1297 'mistak':134,137,1318 'mod':675 'mod.record':679 'model':32,779,791,836,850,1591 'modelconfigur':774,822 'modelconfiguratio':34 'modelcontain':830 'modifi':281 'monitor':998 'must':780,840 'name':194,1029 'need':471,763 'network':1107,1549 'networkfailur':1101,1170 'networkunavail':1102,1171 'newdata':595 'nextrecordzonechangebatch':705 'nil':813,1494 'noaccount':1077,1333,1526 'nodata':587 'notauthent':1129 'note':73,229,233,343,372,411,427,484,494,550,793,823 'note.self':832 'noteid':735 'notes-zone-chang':549 'noteszon':321,547,730 'notif':455,574,579,615,919 'notifi':1124 'notification.userinfo':924 'notificationcenter.default.addobserver':912,1013 'notificationsen':900 'notifinfo':499,506,527,554 'notifinfo.shouldsendcontentavailable':501 'notifyuserstorageful':1190 'nscompoundpred':438 'nsdate':445 'nsmetadataitem':1027 'nsmetadataitemfsnamekey':1010,1032 'nsmetadataqueri':1003 'nsmetadataquerydidfinishgath':1015 'nsmetadataqueryubiquitousdocumentsscop':1012 'nsmetadataubiquitousitemdownloadingstatuskey':1039 'nspredic':350,363,413,429,440,446,486,1005 'nssortdescriptor':376 'nsubiquitouskeyvaluestor':23,106,107,862,1674 'nsubiquitouskeyvaluestore.default':887 'nsubiquitouskeyvaluestore.didchangeexternallynotification':914,1600 'nsubiquitouskeyvaluestorechangedkeyskey':934 'nsubiquitouskeyvaluestorechangereasonkey':928 'nsubiquitouskeyvaluestoreinitialsyncchang':951 'nsubiquitouskeyvaluestorequotaviolationchang':954 'nsubiquitouskeyvaluestoreserverchang':942 'object':915,1016 'observ':909,1601 'older':71 'oper':1323,1636 'optimist':1409 'option':495,782,842,1593 'owner':185 'pair':214 'partial':1195,1200 'partialerrorsbyitemid':1137,1581 'partialfailur':1135,1192,1574 'pass':1493 'pdf':1009 'pend':712,718 'pendingchang':716 'pendingrecordzonechang':739 'per':218,876,1138,1578 'per-item':1577 'perform':1321 'persist':757,1501,1567,1614 'person':1387 'plain':1609 'plan':251 'point':756 'poll':1416 'predic':362,373,374,412,428,435,485 'print':396 'privat':175,523,582,619,825,1384,1529 'private-db-chang':522,581 'private/shared':305 'privateclouddatabase.save':1315 'privatedb':202 'privatedb.deleterecord':299 'privatedb.record':275 'privatedb.records':385 'privatedb.save':258,294,329,509,530,557,1338,1352,1456 'processremotedelet':684 'processremoterecord':678 'prompt':1130 'properti':783 'provid':157 'public':166,1380,1389,1535 'publicdb':199 'pull':746 'pull-to-refresh':745 'push':454,469,504,614,1429,1562 'push-bas':1428,1561 'q3':239 'queri':55,347,369,387,473,1002,1017 'query.disableupdates':1021 'query.enableupdates':1042 'query.predicate':1004 'query.results':1025 'query.searchscopes':1011 'query.sortdescriptors':375 'query.start':1043 'queu':1551 'queue':917,1018,1103 'quota':165,174,180,186 'quotaexceed':1123,1189 'rate':1463 're':1151 're-upload':1150 'read':169,902 'reason':926,940 'recommend':602 'reconcil':1411 'record':53,183,209,219,226,230,236,243,248,259,303,340,348,393,397,403,457,478,532,1217,1228,1264,1339,1353,1370,1629 'recordid':264,277,301,332,344,345,719,723,732,741 'recordnam':266,334,734 'recordtyp':228,342,371,410,426,483 'recreat':1147,1585 'recreatezoneandresync':1208 'reduc':1126 'refer':143,144,1619 'references/cloudkit-patterns.md':1621,1622 'refetch':1143,1573 'refresh':748 'relationship':839,860 'relev':75 'reloadallset':952 'repeat':1437 'report.pdf':990 'requestratelimit':1117,1178 'requir':162,1471 'reset':1141,1571 'resetchangetoken':1205 'resolut':61,129,132,1116,1213 'resolveconflict':1175,1242 'respect':1555 'restrict':1081 'result':382,389,391 'result.get':395 'resum':766 'resync':1588 'retri':610,1105,1120,1460,1553 'retryaftersecond':1122,1554 'return':586,594,714,938,979,1074,1108,1166,1224,1258 'review':3,138,141,1512 'review-checklist':140 'roadmap':240,433 'rule':837 'save':255,283,691,695,1215,1404 'saverecord':740 'schedul':607,724 'scheduleretri':1185 'scheduleretrywhenonlin':1172 'scope':161 'search':419 'searchqueri':424 'see':1114,1620 'self':431,646 'self.body':816 'self.createdat':818 'self.loadstate':644 'self.recordtosend':721 'self.savestate':662 'self.syncengine':647 'self.title':814 'sensit':1602 'sent':689 'sent.failedrecordsaves':699 'sent.savedrecords':693 'sentrecordzonechang':687 'serial':1613 'server':460,1221,1255,1263,1270,1274,1278,1291,1295,1308,1309,1316,1369 'server-sid':459,1220 'serverrecord':1233,1484,1548 'serverrecordchang':1109,1174,1225,1247,1357 'serviceunavail':1119,1180 'setup':52,80,85,148 'share':181,182,315,621,1538 'shareddb':205 'shouldsendcontentavail':1452 'side':461,1222 'sign':153,1133,1519 'sign-in':1132 'silent':503 'simpl':863 'skill' 'skill-cloudkit' 'sosumi.ai':1644,1648,1652,1656,1660,1664,1668,1672,1676,1680 'sosumi.ai/documentation/cloudkit)':1643 'sosumi.ai/documentation/cloudkit/ckasset)':1679 'sosumi.ai/documentation/cloudkit/ckcontainer)':1647 'sosumi.ai/documentation/cloudkit/ckerror)':1671 'sosumi.ai/documentation/cloudkit/ckquery)':1655 'sosumi.ai/documentation/cloudkit/ckrecord)':1651 'sosumi.ai/documentation/cloudkit/ckshare)':1667 'sosumi.ai/documentation/cloudkit/cksubscription)':1659 'sosumi.ai/documentation/cloudkit/cksyncengine)':1663 'sosumi.ai/documentation/foundation/nsubiquitouskeyvaluestore)':1675 'source-dpearson2699' 'specif':1376 'state':1612 'stateseri':643,758 'stateupd':659 'status':118,124,1036,1045,1052,1065,1071,1075,1327,1522 'storag':46,164,1634 'store':878,1373 'strategi':1100 'string':400,421,796,799,810,812,936,1034,1041 'sub':1443,1457 'sub.notificationinfo':1449,1451 'subscript':56,452,474,481,510,512,534 'subscription.notificationinfo':505 'subscriptionid':491,521,548,580,1445 'subsequ':1510 'suppli':1507 'support':309,351,775 'swift':69,187,223,316,360,472,562,624,788,884,971,1058,1154,1240,1334,1432 'swiftdata':31,58,102,104,772,790,1589 'switch':656,939,1070,1167 'sync':9,30,36,111,116,603,744,777,867,961,970,1054,1322,1431,1525,1564,1626 'syncengin':632,654,708 'syncengine.fetchchanges':751 'syncengine.sendchanges':754 'syncengine.state.add':738 'syncengine.state.pendingrecordzonechanges':713 'syncerror.noicloudaccount':1079,1349 'syncerror.restricted':1083 'syncerror.temporarilyunavailable':1087 'syncerror.unknown':1091,1095 'syncmanag':629 'system':908 'tag':249,448,488,1239,1490 'target':65 'task':1311 'temporarilyunavail':1085 'text':418 'theme':892,904,907 'three':158,1111,1227,1362,1544 'three-way':1110,1361,1543 'throw':1062,1078,1082,1086,1090,1094,1348 'timer':1421 'timer.scheduledtimer':1434 'titl':231,285,287,365,398,795,809,815 'token':612,771,1142,1496,1503,1566 'topic-accessibility' 'topic-agent-skills' 'topic-ai-coding' 'topic-apple' 'topic-claude-code' 'topic-codex-skills' 'topic-cursor-skills' 'topic-ios' 'topic-ios-development' 'topic-liquid-glass' 'topic-localization' 'topic-mapkit' 'total':873 'track':313 'transient':608 'tri':256,273,292,297,327,383,394,507,528,555,749,752,829,991,1066,1312,1336,1342,1350,1454 'trigger':453,742 'true':415,502,898,993,1438,1453 'type':406 'ubiqu':964 'ubiquityurl':974 'ubiquityurl.appendingpathcomponent':985 'uiapplic':566 'uibackgroundfetchresult':572 'unavail':883 'unchang':1279,1289 'uniqu':268,786,844,845,1595 'unique-id':267 'unknown':1092 'unsupport':848 'updat':278,286,291,661,1406 'update.stateserialization':663 'upload':1152 'urgent':490,493 'urgent-not':492 'usag':1128,1640 'use':13,40,590,781,852,962,1280,1365,1383,1423,1466,1531,1559,1592,1604 'user':168,177,179,1125,1375,1533 'user-specif':1374 'userdeletedzon':1146,1207,1582 'userinfo':568,577,923,927,933 'uuid':335 'uuidstr':336 'valu':45,213,414,866,877 'var':794,797,800,805 'version':1229 'via':33,1580 'wait':1469 'way':1112,1363,1545 'wide':1394 'withid':300 'within':538 'without':1324 'withtimeinterv':1435 'work':15,250,450,617 'write':171,888 'wrong':1335,1433 'yes':172,178,184 'zone':304,308,323,330,533,540,551,1148,1586,1630 'zonebusi':1118,1179 'zoneid':318,325,326,337,338,544,727,736,737 'zonenam':320,546,729 'zonesub':542,558 'zonesub.notificationinfo':553","prices":[{"id":"80a933e2-3e7f-41f4-8875-da27ac6b96f8","listingId":"ca93e98d-550b-46a2-8452-c8dce326b65b","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dpearson2699","category":"swift-ios-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T22:00:50.224Z"}],"sources":[{"listingId":"ca93e98d-550b-46a2-8452-c8dce326b65b","source":"github","sourceId":"dpearson2699/swift-ios-skills/cloudkit","sourceUrl":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/cloudkit","isPrimary":false,"firstSeenAt":"2026-04-18T22:00:50.224Z","lastSeenAt":"2026-04-22T00:53:42.072Z"}],"details":{"listingId":"ca93e98d-550b-46a2-8452-c8dce326b65b","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dpearson2699","slug":"cloudkit","github":{"repo":"dpearson2699/swift-ios-skills","stars":468,"topics":["accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills","ios","ios-development","liquid-glass","localization","mapkit","networking","storekit","swift","swift-concurrency","swiftdata","swiftui","widgetkit","xcode"],"license":"other","html_url":"https://github.com/dpearson2699/swift-ios-skills","pushed_at":"2026-04-21T19:26:16Z","description":"Agent Skills for iOS 26+, Swift 6.3, SwiftUI, and modern Apple frameworks","skill_md_sha":"1df0c9bdb9e8099298673ba14ed08c2c59a1dd3b","skill_md_path":"skills/cloudkit/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/cloudkit"},"layout":"multi","source":"github","category":"swift-ios-skills","frontmatter":{"name":"cloudkit","description":"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 cloudKitDatabase; when handling CKError codes for conflict resolution, network failures, or quota limits; or when checking iCloud account status before performing sync operations."},"skills_sh_url":"https://skills.sh/dpearson2699/swift-ios-skills/cloudkit"},"updatedAt":"2026-04-22T00:53:42.072Z"}}