CloudKit Sync Stalls During Initial Large Data Hydration on New Device (SwiftData Local-First Architecture)

Hi everyone,

I’m facing an issue with CloudKit sync getting stuck during initial device migration in my SwiftData-based app.

The app follows a local-first architecture using SwiftData + CloudKit sync, and works correctly for:

✔ Incremental sync ✔ Bi-directional updates ✔ Small datasets

However, when onboarding a new device with large historical data, sync becomes extremely slow or appears stuck. Even after two hours data is not fully synced. ~6900 Transactions

🚨 Problem

When installing the app on a new iPhone and enabling iCloud sync: • Initial hydration starts • A small amount of data syncs • Then sync stalls indefinitely

Observed behaviour: • iPhone → Mac sync works (new changes sync back) • Mac → iPhone large historical migration gets stuck • Reinstalling app / clearing container does not resolve issue • Sync never completes full migration

This gives the impression that:

CloudKit is trickling data but not progressing after a certain threshold.

The architecture is: • SwiftData local store • Manual CloudKit sync layer • Local-first persistence • Background push/pull sync

So I understand:

✔ Conflict resolution is custom ✔ Initial import may not be optimized by default

But I expected CloudKit to eventually deliver all records.

Instead, the new device remains permanently in a “partial state”.

🔍 Observations • No fatal CloudKit errors • No rate-limit errors • No quota issues • iCloud is available • Sync state remains “Ready” • Hydration remains “mostlyReady”

Meaning:

CloudKit does not report failure — but data transfer halts.

🤔 Questions

Would appreciate guidance on:

  1. Is CloudKit designed to support large initial dataset migration via manual sync layers?

Or is this a known limitation vs NSPersistentCloudKitContainer?

  1. Does CloudKit internally throttle historical record fetches?

Could it silently stall without error when record volume is high?

  1. Is there any recommended strategy for: • Bulk initial migration • Progressive hydration • Forcing forward sync progress

  1. Should initial migration be handled outside CloudKit (e.g. via file transfer / backup restore) before enabling sync?

🎯 Goal

I want to support: • Large historical onboarding • Multi-device sync • User-visible progress

Without forcing migration to Core Data.

🙏 Any advice on: • Best practices • Debugging approach • CloudKit behavior in such scenarios

would be greatly appreciated.

Thank you!

For a large dataset, the initial synchronization with CloudKit, which can happen in several cases, including on-boarding a new device, can indeed be slow, or even failed. A similar issue was discussed in this thread as well.

Depending on your concrete use case, you might address the issue in the following way:

  • Have your app functional before the dataset is fully synchronized.
  • Synchronize the data in batches in the background, until the whole data set is done.

As an example, an email app that is just installed on a new device allows users to compose a new email, while synchronizing existing emails in the background.

When using the CloudKit framework, you control what kind and amount of data to be synchronized with CloudKit, and hence can implement the strategy, though the implementation can be quite involved.

Now that you are using SwiftData + CloudKit integration, NSPersistentCloudKitContainer takes care the synchronization process. I don't really see anything to work around or mitigate the issue in this case.

I’d suggest that you file a feedback report for the CloudKit folks to investigate the way to improve the initial synchronization for a large data set – If you do so, please share your report ID here. Thanks.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

CloudKit Sync Stalls During Initial Large Data Hydration on New Device (SwiftData Local-First Architecture)
 
 
Q