I have transitioned to CKSyncEngine
for syncing data to iCloud, and it is working quite well. I have a question regarding best practices for modifying and saving a CKRecord
which already exists in the private or shared database.
In my current app, most CKRecords
will never be modified after saving to the database, so I do not persist a received record locally after updating my local data model. In the rare event that the local data for that record is modified, I manually fetch the associated server record from the database, modify it, and then use CKSyncEngine
to save the modified record.
As an alternative method, I can create a new CKRecord locally with the corresponding recordID and the modified data, and then use CKSyncEngine to attempt to save that record to the database. Doing so generates an error in the delegate method handleSentRecordZoneChanges
, where I receive the local record I tried to save back inevent.failedRecordSaves
with a .serverRecordChanged
error, along with the corresponding server CKRecord
. I can then update that server record with the local data and re-save using CKSyncEngine. I have not yet seen any issues when doing it this way.
The advantage of the latter method is that CKSyncEngine handles the entire database operation, eliminating the manual fetch step. My question is: is this an acceptable practice, or could this result in other unforeseen issues?
The CKSyncEngine
relies on the .ifServerRecordUnchanged save policy which states:
The server maintains a change tag for each record automatically. When you fetch a record, that change tag accompanies the rest of the record’s data. If the change tag in your local record matches the change tag of the record on the server, the save operation proceeds normally. If the server record contains a newer change tag, CloudKit doesn’t save the record and reports a CKError.Code.serverRecordChanged error.
This implies that to successfully save an existing record via CKSyncEngine
you must return an existing record with it's system fields, see encodeSystemFields(with:). In general it's recommended to save said CKRecord
alongside your local database, using it to generate changes that will be saved to the server.
So while your approach will work it's suboptimal and could potentially lead to data loss if incorrectly implemented.