As I Learn CloudKit Syncing - Part 4: Tracking Local Changes
I’m syncing games. Game data can change in three ways: A new game, a change to an existing game, and a deleted game. All three are tracked differently.
Tracking new games
I can know if a game is new based on whether it is already linked to a CKRecord. According to the docs, the way you link local data to a CKRecord is through encoding the CKRecord’s metadata with the
-encodeSystemFieldsWithCoder method on
At first I was saving the encoded metadata along with the game data in the Game table. It was a BLOB column1 called
FCModel will store BLOB data as an instance of NSData. Here is how I turned the CKRecord into NSData to store in the Game table:
1 2 3 4 5 6 7 8
And here is how I converted it back from an instance of NSData to a CKRecord:
1 2 3 4
This worked well until I implemented the “Fetch server changes with CKFetchRecordChangesOperation” step of the CloudKit Syncing recipe2.
In this step, at some point I need to query the Game table and lookup a specific game, and all I have is a
CKRecord instance. I can’t search the
remoteRecord BLOB column for the CKRecord because it’s been encoded into a bag of bytes.
I also can’t query the Game table by the gameID (that’s the Game table’s primary key column) which is stored in the
CKRecord. Even though the gameID is one of the Game properties I’m saving on the CKRecord. Why?
It has to do with how
CKFetchRecordChangesOperation works3. As its name implies, it only pulls in changes. So for example if a game’s record on iCloud has its
guesses value changed, then the
CKRecord returned by
CKFetchRecordChangesOperation will only include the new value for
guesses but not any of the other values on that
So the way I’ve worked around this is to create a new table called
RemoteRecord that has two fields, a
remoteRecordID and a
remoteRecord. Now the
CKRecord instance is encoded into the
remoteRecord column of
RemoteRecord, and the local Game table has a
remoteRecordID foreign key relationship to the RemoteRecord table.
The thing that makes it possible to query a local Game from a CKRecord is what I’m using as the
remoteRecordID. Every CKRecord has a CKRecordID associated with it, and that CKRecordID has a property called
recordName which is a string identifier that doesn’t exceed 255 characters. By using the
recordName as the value of the
remoteRecordID, I can now easily lookup a local Game from a CKRecord, kind of like this:
1 2 3
I did consider just putting the
remoteRecordID column on the Game table and not creating the RemoteRecord table at all, but storing the CKRecord in a seperate table from Game made some things easier when tracking changes to a local game.
So to bring this back around to how I’m tracking new local games, with this setup, all I have to do is query for Games that don’t have a remoteRecordID set, like this:
In the next installment I’m going to cover how I’m tracking local changes to existing games.
Get notified of new posts by following me on Twitter.