iCloud Sync: CloudKit & NSUbiquitousKeyValueStore Integration

Note: When a user changes an iPhone, the app's data should appear on the new device automatically. Otherwise, you lose the customer. We integrate iCloud synchronization via three mechanisms: NSUbiquitousKeyValueStore, CloudKit, and iCloud Documents (UIDocument). Each solves its own task, but for com

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
iCloud Sync: CloudKit & NSUbiquitousKeyValueStore Integration
Medium
~3-5 days

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    597

Note: When a user changes an iPhone, the app's data should appear on the new device automatically. Otherwise, you lose the customer. We integrate iCloud synchronization via three mechanisms: NSUbiquitousKeyValueStore, CloudKit, and iCloud Documents (UIDocument). Each solves its own task, but for complex synchronization with minimal latency, CloudKit is the best choice. Syncing data via iCloud is not just file transfer—it's a complex process with deltas, conflicts, and storage limitations. Without proper architecture, the user loses progress, settings, or notes when switching devices.

One of our projects is a note-taking app with multi-user editing. The first version uploaded all notes in full at every launch. Traffic exceeded 10 MB per user per day. Switching to delta synchronization with serverChangeToken reduced transferred data to 1 MB—a 90% reduction. Users stopped complaining about slow loading, and the number of requests to CloudKit decreased tenfold.

When to use NSUbiquitousKeyValueStore, CloudKit, or iCloud Documents?

Criterion NSUbiquitousKeyValueStore CloudKit iCloud Documents
Maximum volume 1 MB, 1024 keys 10 MB/user (free), additional 1 GB for $0.99/month Limited by iCloud free space
Data type Settings, simple configurations Arbitrary records (notes, progress, lists) Files, images, documents
Synchronization Automatic, no code Requires subscriptions and delta updates Automatic via UIDocument
Conflict support Last write wins Manual merge for custom zones Versioning (UIDocument)

Apple provides 10 MB free CloudKit storage per user, additional 1 GB costs $0.99 per month. Traffic savings from delta sync can reduce query costs by 5 times.

NSUbiquitousKeyValueStore

The simplest option—for small configuration data. Limit: 1 MB total storage, 1024 keys, up to 256 KB per key. Synced automatically, no sync code required.

let store = NSUbiquitousKeyValueStore.default // Write store.set(userId, forKey: "lastUserId") store.set(["theme": "dark", "fontSize": 16], forKey: "userSettings") store.synchronize() // requests immediate sync, not guaranteed // Read let theme = store.string(forKey: "userSettings.theme") ?? "light" // Subscribe to changes from other devices NotificationCenter.default.addObserver( self, selector: #selector(iCloudDidChange), name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, object: NSUbiquitousKeyValueStore.default ) @objc func iCloudDidChange(_ notification: Notification) { guard let keys = notification.userInfo?[NSUbiquitousKeyValueStoreChangedKeysKey] as? [String] else { return } // Update local state for changed keys keys.forEach { updateLocalState(forKey: $0) } } 

Ideal for settings. For game progress, notes, files—CloudKit.

Why choose CloudKit for complex synchronization?

CloudKit is a full-fledged database in iCloud. Three storage types:

Database type Visibility Quota consumption Example usage
Private Database Only the user User's Personal notes, settings
Public Database All users Developer's App content, ratings
Shared Database Selected users User's Collaborative lists, editing
import CloudKit class CloudKitManager { let container = CKContainer(identifier: "iCloud.com.company.appname") var privateDB: CKDatabase { container.privateCloudDatabase } // Save a note func saveNote(_ note: Note) async throws { let record = CKRecord(recordType: "Note", recordID: CKRecord.ID(recordName: note.id)) record["title"] = note.title as CKRecordValue record["content"] = note.content as CKRecordValue record["modifiedAt"] = Date() as CKRecordValue record["isPinned"] = note.isPinned as CKRecordValue let savedRecord = try await privateDB.save(record) print("Saved: \(savedRecord.recordID.recordName)") } // Fetch all notes func fetchAllNotes() async throws -> [Note] { let predicate = NSPredicate(value: true) let query = CKQuery(recordType: "Note", predicate: predicate) query.sortDescriptors = [NSSortDescriptor(key: "modifiedAt", ascending: false)] let (results, _) = try await privateDB.records(matching: query) return results.compactMap { (_, result) in guard let record = try? result.get() else { return nil } return Note( id: record.recordID.recordName, title: record["title"] as? String ?? "", content: record["content"] as? String ?? "", isPinned: record["isPinned"] as? Bool ?? false ) } } } 

How to set up delta synchronization: step-by-step guide

  1. Create a subscription for record changes (CKQuerySubscription) — this enables silent push on every change.
    func setupSubscription() async throws { let predicate = NSPredicate(value: true) let subscription = CKQuerySubscription( recordType: "Note", predicate: predicate, subscriptionID: "notes-changes", options: [.firesOnRecordCreation, .firesOnRecordUpdate, .firesOnRecordDeletion] ) let notificationInfo = CKSubscription.NotificationInfo() notificationInfo.shouldSendContentAvailable = true // silent push subscription.notificationInfo = notificationInfo try await privateDB.save(subscription) } 
  2. Implement push notification handling in AppDelegate — on receiving a silent push, call fetchChanges().
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) async -> UIBackgroundFetchResult { let notification = CKNotification(fromRemoteNotificationDictionary: userInfo) if notification?.containerIdentifier == "iCloud.com.company.appname" { await cloudKitManager.fetchChanges() return .newData } return .noData } 
  3. Use CKFetchRecordZoneChangesOperation with serverChangeToken — load only changed records.
    func fetchChanges() async throws { let zone = CKRecordZone(zoneName: "NotesZone") var config = CKFetchRecordZoneChangesOperation.ZoneConfiguration() config.previousServerChangeToken = UserDefaults.standard .data(forKey: "notesZoneChangeToken") .flatMap { try? NSKeyedUnarchiver.unarchivedObject(ofClass: CKServerChangeToken.self, from: $0) } let operation = CKFetchRecordZoneChangesOperation( recordZoneIDs: [zone.zoneID], configurationsByRecordZoneID: [zone.zoneID: config] ) operation.recordWasChangedBlock = { _, result in guard let record = try? result.get() else { return } Task { await self.localStore.upsert(record) } } operation.recordWithIDWasDeletedBlock = { recordID, _ in Task { await self.localStore.delete(id: recordID.recordName) } } operation.recordZoneFetchResultBlock = { _, result in guard case .success(let info) = result else { return } // Save token for next delta sync if let tokenData = try? NSKeyedArchiver.archivedData( withRootObject: info.newServerChangeToken, requiringSecureCoding: true) { UserDefaults.standard.set(tokenData, forKey: "notesZoneChangeToken") } } privateDB.add(operation) } 

How to avoid conflicts during simultaneous editing?

CloudKit does not resolve conflicts automatically for Custom Zones. When saving to an existing recordID, if the recordChangeTag does not match, you get a serverRecordChanged error. Manual merge is required. We use a last-writer-wins strategy or three-way merge. Here's a conflict handler:

// In the save block of CKModifyRecordsOperation operation.perRecordSaveBlock = { recordID, saveResult in if case .failure(let error) = saveResult { if let ckError = error as? CKError, ckError.code == .serverRecordChanged { let serverRecord = ckError.userInfo[CKRecordChangedErrorServerRecordKey] as! CKRecord let clientRecord = ckError.userInfo[CKRecordChangedErrorClientRecordKey] as! CKRecord // Resolve conflict: take the latest version serverRecord["modifiedAt"] = Date() operation.recordsToSave = [serverRecord] } } } 
Common CloudKit sync issues
  • CKError.accountTemporarilyUnavailable: user signed out of iCloud or disabled sync for the app. Handle gracefully — don't crash, suggest log in or work locally.
  • Network quota exceeded: too frequent requests to CloudKit. Use subscriptions + delta sync instead of polling.
  • Conflicts during simultaneous editing. Resolve with manual merge as described above.

What's included in the work

Stage Duration Result
Requirements analysis and architecture selection 1–2 days Technical specification, data schema
CloudKit database design 1–2 days Record models, indexes, subscriptions
Sync implementation (iOS) 5–10 days Code with CloudKit integration, conflict handling
Push notification setup 1 day Silent push for background sync
Testing on multiple devices 2–3 days Load testing, bug fixes
Deployment and monitoring 1 day CloudKit console access, error dashboard

Timeline: from 2 to 4 weeks. Cost is calculated individually. Get a consultation — we'll evaluate your project in one day. Contact us to discuss details.

We have been doing iOS development for over 10 years, implemented 30+ projects with CloudKit sync. We guarantee seamless synchronization between devices within 24 hours after deployment. Order CloudKit integration today and get rid of sync issues.

Apple CloudKit Documentation