iCloud Data Synchronization Implementation

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
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 1 servicesAll 1735 services
iCloud Data Synchronization Implementation
Medium
~3-5 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1050
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Implementing Data Synchronization via iCloud

iCloud — Apple's built-in sync platform, accessible without third-party registration. iOS user expects app to remember their data after phone switch and sync between iPhone and iPad. Developer's task — choose right mechanism from three available: NSUbiquitousKeyValueStore, CloudKit and iCloud Documents (via UIDocument).

NSUbiquitousKeyValueStore

Simplest option — for small configuration data. Limit 1 MB total storage, 1024 keys, up to 256 KB per key. Syncs automatically, no sync code.

let store = NSUbiquitousKeyValueStore.default

// Write
store.set(userId, forKey: "lastUserId")
store.set(["theme": "dark", "fontSize": 16], forKey: "userSettings")
store.synchronize() // requests immediate sync, doesn't guarantee

// 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) }
}

For syncing settings, small user data — ideal. For game progress, notes, files — CloudKit.

CloudKit: Public, Private, Shared Database

CloudKit — full database in iCloud. Three storage types:

  • Private Database — user data, visible only to them. Consumes user's iCloud quota, not yours.
  • Public Database — app data, accessible to all. Consumes your developer quota.
  • Shared Database — for "share with user", collaborative editing features.
import CloudKit

class CloudKitManager {
    let container = CKContainer(identifier: "iCloud.com.company.appname")
    var privateDB: CKDatabase { container.privateCloudDatabase }

    // Save 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)")
    }

    // Load 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
            )
        }
    }
}

CKSubscription: Push Notifications on Change

When user changes data on iPad, iPhone should know immediately. CKQuerySubscription subscribes to record changes and sends silent push:

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)
}

// In AppDelegate / UNUserNotificationCenterDelegate
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
}

Silent push wakes app in background (if Background App Refresh allowed) and app pulls changes.

CKFetchRecordZoneChangesOperation: Efficient Delta Sync

Requesting all records on each sync — inefficient. CKFetchRecordZoneChangesOperation returns only changes since last sync via serverChangeToken:

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)
}

Without serverChangeToken you download everything. With token — only delta.

Typical Problems

CKError.accountTemporarilyUnavailable. User signed out of iCloud or disabled sync for app. Need handling — don't crash, offer to sign in or work locally.

Network quota exceeded. Too frequent CloudKit requests. Apple limits request rate. Use subscriptions + delta sync instead of polling.

Conflicts on simultaneous editing. CloudKit doesn't auto-resolve for Custom Zones. On save to existing recordID, if recordChangeTag doesn't match — error serverRecordChanged. Need manual merge.

Implementing CloudKit sync with CKSubscription, delta sync and conflict handling: 2–4 weeks. Cost calculated individually.