CKSyncEngine questions

I've been building an app with CKSyncEngine based off the documentation and sample code on GitHub. So far it's been working great, but I still have a number of questions I couldn't find the answer to I'd like to know before going into production. Here's a list in no particular order:

  1. When sending changes, are you expected to always send the entire record or just the fields that changed? It's not clear there would even be a way to know the fields that changed since when we have to populate the CKRecord from our local record, we only know the id.
  2. Likewise, when we get a record that changed on the server, do we always get a complete record even if only a single field changed?
  3. Related to that, if a record has asset(s), is the complete asset also returned on every server change even if we already have a copy locally and it hasn't been modified?
  4. If a record does have an asset, is the asset guaranteed to be downloaded and available at the asset.fileURL location by the time CKSyncEngine calls the delegate? If not, is there a way to know it's still downloading and when it will be available?
  5. Is there a way to lazy load assets to avoid unnecessary data fetching?
  6. If there is a failure during sync, for example if I fail to save just one record out of many, how do I recover from that? Is there a way to retry?
  7. Related, is there a way to verify we're completely in sync with the server?
  8. Is there any way to resync besides deleting the state serialization data and doing a complete sync again?
  9. Can I use CKSyncEngine from the main app and the app extensions if they share a database and state serialization. For example, when adding an image from the share extension. Any caveats to that?

Sorry for all the questions, but I want to make sure this is as efficient and reliable as possible. I'm going to request a Lab as well, but it's the lab request form isn't working at the moment so I figured I'd post here in case it's easier to answer async.

Thanks! – Zach

Wow, a lot of questions! I'd answer based on my current impression. If I see something inaccurate later on, I'd correct it with another reply.

When sending changes, are you expected to always send the entire record or just the fields that changed? It's not clear there would even be a way to know the fields that changed since when we have to populate the CKRecord from our local record, we only know the id.

Likewise, when we get a record that changed on the server, do we always get a complete record even if only a single field changed?

Yes and yes. CloudKit always accepts or delivers a full record.

CKSyncEngine doesn't upload unchanged data when saving a record though. When you fetch a record (CKRecord) from the CloudKit server and change it locally, CloudKit tracks the changed fields (changedKey). If you save the changed record by adding a pending change and returning that record in nextRecordZoneChangeBatch(_:syncEngine:), CKSyncEngine will figure out the changed fields and only upload the changes.

Related to that, if a record has asset(s), is the complete asset also returned on every server change even if we already have a copy locally and it hasn't been modified?

Yes. CloudKit doesn't know if you cache the data or not, and so returns the full record.

If a record does have an asset, is the asset guaranteed to be downloaded and available at the asset.fileURL location by the time CKSyncEngine calls the delegate? If not, is there a way to know it's still downloading and when it will be available?

Yes. CloudKit guarantees that fileURL points to a valid file when the delegate method (handleEvent(_:syncEngine:)) is called. The file may be removed after the delegate method returns. If that can be an issue for your app, consider moving the file to your own folder when receiving the record.

Is there a way to lazy load assets to avoid unnecessary data fetching?

There isn't. Consider putting your asset fields into a separate record type. Feel feel to file a feedback report though.

If there is a failure during sync, for example if I fail to save just one record out of many, how do I recover from that? Is there a way to retry?

The event (CKSyncEngine.Event) should return enough information for this kind of error handling. For example, when you save some records and hit an error, CKSyncEngine.Event.SentRecordZoneChanges event returns you the failed saves (.failedRecordSaves), and you an handle the error from there. For more information, see CloudKit Samples: CKSyncEngine.

Related, is there a way to verify we're completely in sync with the server?

There isn't. When you do a fetch and the CloudKit server returns a changed record, before you receives it, the record on the server can change again.

Is there any way to resync besides deleting the state serialization data and doing a complete sync again?

CKSyncEngine synchronizes data based on the state. You can probably maintain multiple synchronization points by maintaining multiple state serializations (CKSyncEngine.State.Serialization).

Can I use CKSyncEngine from the main app and the app extensions if they share a database and state serialization. For example, when adding an image from the share extension. Any caveats to that?

I haven't tried, but probably won't do that. Typically, you don't control the life cycle of an extension, and so it is perfectly possible that your main app and extensions are running simultaneously. Sharing a same state serialization may trigger a conflict in that case.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

CKSyncEngine questions
 
 
Q