CloudKit Sync Not Working? Diagnose Setup, Export, and Import
Diagnose CloudKit sync failures by separating local saves, exports, imports, and UI updates. Includes account, schema, conflict, and two-device checks.
When a record saves on one device but does not appear on another, first identify the stage that failed: local persistence, export to CloudKit, import from CloudKit, or presentation of the imported data. Rebuilding the persistence stack or deleting the store before collecting evidence can hide the cause and risk unsynchronized data.
This troubleshooting path focuses on NSPersistentCloudKitContainer. For initial configuration, use the Core Data and CloudKit setup guide. For a comparison of database scopes, read private versus shared CloudKit databases.
First identify who owns synchronization
The diagnosis depends on your stack:
- Core Data with NSPersistentCloudKitContainer: Core Data manages mirroring. Investigate its store configuration, synchronization events, and logs.
- SwiftData with CloudKit: inspect
ModelConfiguration, schema compatibility, and supported synchronization behavior. Do not attach a second mirroring container to its store as a repair technique. - A custom local store with CKSyncEngine: your delegate supplies records and persists synchronization state. Check both responsibilities when progress stops.
- Direct CloudKit operations: your application owns change tracking, retries, and conflict handling.
Apple's CKSyncEngine documentation describes support for private and shared databases; it is not a public-database sync engine. Choose a stack deliberately rather than mixing owners for the same records.
1. Prove the local save succeeded
Read the record back from the persistent store after saving. Check for validation and store-loading errors. A value displayed in memory is not evidence that the save reached disk.
Keep local writes independent of network availability. A successful local save means the device has retained the change; it does not mean another device has received it. Make that distinction visible in any sync status you present.
For a reproducible report, record a non-sensitive test identifier, the save time, app version, and store configuration. Avoid logging customer text or entire record payloads.
2. Verify account, container, and environment
For private-database synchronization, use two devices signed into the same iCloud account. Different accounts have different private databases. Two accounts belong in a sharing test that includes creating and accepting a CKShare.
Check that both builds use the intended iCloud container and CloudKit environment. Verify capabilities and signed entitlements, rather than relying only on the settings visible in the Xcode project. A development build and a distributed build may be looking at different environments.
Check iCloud account availability, the app's iCloud setting, and storage-related errors. Preserve locally saved data while synchronization is unavailable. An account change needs an explicit product policy; silently deleting a store is not a safe generic response.
3. Locate export or import failure
Observe NSPersistentCloudKitContainer.eventChangedNotification and correlate setup, export, and import events with the test record. Capture the event's completion and error information. An event starting is not evidence that it succeeded, and an export completing does not prove every recipient has imported it.
Use CloudKit Console to inspect the matching environment, database, and record zone. If the record is absent remotely, investigate export. If it is present remotely but absent in the second device's store, investigate import.
Apple's synchronization explanation and debugging technote provide the framework-specific logging workflow. Sync is scheduled asynchronously; do not promise a fixed delivery deadline or implement a timer that repeatedly recreates the container.
4. Separate UI merging from cloud conflict resolution
If the imported object exists in the store but the screen is stale, inspect the reading context and its merge behavior. automaticallyMergesChangesFromParent can help a view context incorporate changes saved through the coordinator. A context's merge policy resolves conflicts between that context's values and its persistent-store snapshots.
That policy is not a universal cross-device conflict algorithm. Setting object-trump does not establish that the last user action across all devices wins, and it does not guarantee preservation of two offline edits.
Write conflict requirements in terms of user operations. Two people adding separate comments should not overwrite one shared text field. A balance derived from independent transactions has different conflict needs from an editable document title. Test your chosen data representation with offline edits and inspect the converged result. See the conflict-resolution analysis for the next level of design review.
5. Check schema and distribution differences
A debug build working locally does not establish that the production CloudKit schema supports the distributed app. Verify the required record fields and indexes in the intended environment before release.
Initialize and inspect development schemas using Apple's documented tooling, then follow the production deployment workflow. Do not reset the development environment before every test: that can conceal migration and compatibility problems. Never use a reset or local-store deletion as the first troubleshooting step for customer data.
Run a small release test matrix
- Same-account private sync: create a unique test record on device A; verify its export, import, and presentation on device B.
- Offline editing: change data without connectivity, relaunch the app, and confirm local persistence before reconnecting.
- Conflicting changes: edit the same logical item offline on both devices; reconnect and verify the defined result without losing unrelated fields.
- Account unavailable: confirm local work remains usable and the UI does not falsely claim remote completion.
- Sharing, if supported: use separate accounts, accept an invitation, test permissions, then test revocation.
- Distribution: repeat with the release environment and an upgrade from a supported previous app version.
An in-memory test can verify local transformation logic. It cannot demonstrate iCloud delivery, background scheduling, or production entitlement correctness. Keep those results separate in your release evidence.
If you need help identifying which boundary is failing, an iOS architecture audit can scope the investigation around a reproducible sync scenario rather than a speculative rewrite.