This is the third in an eight-part series on implementing data sharing in Shopping UK using CloudKit.
Shopping UK is a smart shopping list for UK shoppers. It knows almost every product in the supermarket and will arrange them by aisle. Lists can be shared with family or friends.
Last week, we looked at CloudKit concepts and the design of the Shopping UK schema. Today, we’ll look at how CKRecords are uploaded to iCloud using CloudKit; and the three ways the app can fetch CKRecords.
Let’s look at what happens when a user adds two new items to the list.
There are three ways a user can add items to the list:
It doesn’t matter how the user added “milk” and “bread” to their shopping list, the way they are uploaded to iCloud via CloudKit is the same.
Shopping UK maintains a local journal of changes. All changes are first written to this journal before the app sends them to iCloud.
This step is not essential for uploading data to iCloud using CloudKit but, if your app must guarantee changes will be uploaded, it is wise to first store them on the device, even if only temporarily, before uploading the changes to iCloud.
By doing this, we can be sure nothing will be lost if the network is not available. We’ll examine how things can fail in depth in part 8.
Note: The app can set a Quality of Service (QoS) value for the CKModifyRecordsOperation. A lower level QoS is used for operations that are not time critical. iOS may delay these operations when the device is in low power mode or low on battery.
Shopping UK uses the User-Initiated level for sending and fetching JournalEntry records. This provides nearly instantaneous messaging (a few seconds or less).
All CloudKit operations are asynchronous and typically follow this pattern:
The happy path is shown here — when everything goes well — but, due to the nature of networks and remote systems, things can go wrong. In part 8, we will revisit the error scenarios and how Shopping UK handles each one.
We looked at uploading new CKRecords to iCloud, but how is data fetched?
There are three options:
|Type||Operation to use|
|Fetch By Name||CKFetchRecordsOperation|
|Fetch By Query||CKQueryOperation|
Shopping UK uses all three techniques in different places.
Fetch By Name is used to:
Fetch By Query is used to fetch JournalEntry records eligible for compression (more on this in part 7).
Fetch Changes is the most interesting of the three. It uses a CKServerChangeToken to identify each version in the database’s history, which means the app can request just the records that changed since its last request. This is much more efficient than fetching everything every time, and it is more reliable than creating a custom mechanism for tracking changes.
For my first attempt at implementing sharing using CloudKit, before I understood how Fetch Changes worked, I tried to build my own change tracking mechanism using timestamps. I spent a long time on this dead-end, but it didn’t work well due to the precision of timestamps, and tiny differences in the clock setting of each device
Shopping UK uses Fetch Changes to retrieve changes made by other devices participating in a shared list.
Assume two CKRecords already exist in iCloud and this is the first time the white iPhone is requesting changes.
Since this is the first time data will be fetched, the device won’t have a
so it will send
null for the
token’s value. This instructs CloudKit to return all changes ever made.
CloudKit will return three response messages:
If multiple zones had changed, there would be a Changed Record Zone response for each zone that contained a change.
If any zones had been deleted, an additional Deleted Record Zone response would have been returned too.
The database change token is stored so it can be used next time CKFetchDatabaseChangesOperation is sent.
Since this is the first time this has been issued, the app doesn’t yet have a
so it sends
CloudKit will send a separate
for every record added or modified since the Zone CKServerChangeToken.
Because this is the first time this operation has been used, and the token was set to
null, all records (milk and bread) will be returned.
A single Fetch Completed response will be sent at the end.
If a record had been deleted since the last fetch, a Record Id Deleted response would also be received by the app.
The app uses the received CKRecords to update its local representation of the list, and it updates user interface with the new items.
CloudKit’s Fetch Changes mechanism provides an efficient way of synchronising the app’s local cache of the iCloud data. Instead of fetching everything every time, only the recent changes need to be fetched.
In part 6 we’ll look at how the Fetch Changes request can be triggered: when the app receives a remote notification from a CloudKit Subscription or when the user explicitly triggers the list synchronisation.
And, in part 7, we’ll look at situations when the app must fetch everything, such as when the user signs into a new iCloud account.
Next week, we’ll see how sharing works, and how to share a list for the first time.