Supporting CloudKit sync for SQLite is a daunting task. There are edge cases and nuances to be aware of, and the risk of introducing subtle bugs that cause data loss is very real.

Here’s the most important technique we used to build this feature…

Debugging the library by literally running multiple devices and editing data to see how it synchronizes is an absolute time suck. So we built our own in-memory version of CloudKit containers and databases so that we could interact with “CloudKit” in a testable and controllable manner.

This allowed us to write hundreds of tests for many intricate and subtle syncing situations (and turn bug reports from our beta users into failing tests), all without running on a real device or interacting with CloudKit:

• What happens when two devices edit the same record at the same time?

• What happens when a device receives a child record from CloudKit before it receives the parent record? Can foreign key constraints be enforced?

• What happens if a device receives a record from another device running an older version of the schema? Do we lose that data or can it be preserved until the device upgrades to the new schema?

• What happens on first launch of an existing app that has just added CloudKit synchronization? Can we upload all of the existing, local data to CloudKit?

• What happens to the user’s data when they are logged out of iCloud?

• What happens when a user writes to a shared record for which they do not have permissions?

• What happens when one device adds a child record to a parent record and another device deletes the parent record?

• What happens when a record in a shared zone is moved to another zone? Do all associated descendant get moved to the new zone too?

Whenever we wrote a passing test, we could immediately confirm the fix on-device using the live iCloud servers in place of our in-memory CloudKit.

And to be honestly, this is barely scratching the surface of what problems we came across while working on this and what we needed to write tests for.

See the full test suite here:

https://github.com/pointfreeco/sqlite-data/tree/main/Tests/SQLiteDataTests/CloudKitTests

sqlite-data/Tests/SQLiteDataTests/CloudKitTests at main · pointfreeco/sqlite-data

A fast, lightweight replacement for SwiftData, powered by SQL and supporting CloudKit synchronization. - pointfreeco/sqlite-data

GitHub
@pointfreeco is your in memory CloudKit shim able to be used separately from your library? Could I use it to help test _my_ sync engine?

@amyworrall It's possible, but hard to say. The files are in this directory: https://github.com/pointfreeco/sqlite-data/tree/main/Sources/SQLiteData/CloudKit/Internal

In particular, the CloudContainer and CloudDatabase protocols, as well as the MockCloudContainer and MockCloudDatabase. These were built as drop-in replacements for containers and database in tests, but not sure how decoupled it is from the overall library.

sqlite-data/Sources/SQLiteData/CloudKit/Internal at main · pointfreeco/sqlite-data

A fast, lightweight replacement for SwiftData, powered by SQL and supporting CloudKit synchronization. - pointfreeco/sqlite-data

GitHub