Embracing CloudKit: Part 2

Posted by Stuart Wheelwright on May 08, 2023 · 7 mins read

Part 2: What is CloudKit?

This is the second 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 introduced Shopping UK and discussed limitations of its old home-grown data sharing solution. Today, we’ll look at CloudKit’s basic concepts and how the Shopping UK schema is designed.

Concepts

We’ll start with a quick review of the core CloudKit concepts.

If you’re already familiar with the concepts, please skip to the “Creating a CloudKit Schema” section below, which explains how CloudKit is used in Shopping UK.

CloudKit is one of Apple’s key technologies for sharing data in the cloud. Apple offers three options for data sharing:

  1. iCloud Document Storage for file or document synchronisation.
  2. iCloud Key-Value Storage for small key-value pairs.
  3. CloudKit for sharing complex objects.

CloudKit is a way to move structured data between your app and iCloud. It also has a web-based dashboard for viewing and manipulating the data in your own iCloud account (this is really useful for testing).

CloudKit Concepts

CloudKit is free to use. It is natively supported in iOS and it also has a JavaScript API so it can also be used from web sites and outside the Apple ecosystem.

Let’s look at three key concepts: Records, Record Types and Record Zones.

Records

Data is stored in a CKRecord. Think of these like records in a relational database system.

Each CKRecord has a Record Type — a template for defining what values the CKRecord can contain. A Record Type has a name and a list of Keys. Each key is assigned a data-type (e.g String, Date, Bool).

Think of the Record Type like a table in a relational database system, and the Keys as fields.

Record Types

To store books by an author, we might create two Record Types: Book and Author. A Book has a Title, a PublishedOn date, a number of Pages, an ISBN number and a link to an Author:

Example of CloudKit Record Types

Together, the Record Type describe the “shape” of the data and how it links together — its schema. The data itself is represented by a CKRecord. This example uses a couple of my books from one of my favourite childhood authors, Roald Dahl:

Example of CloudKit records

Record Zones

A CKRecordZone is a grouping of CKRecords.

Example of a CKRecordZone

All CKRecords live in one and only one CKRecordZone.

A CKRecordZone lives in a CKDatabase. There are three databases: Public, Private and Shared (more on these later).

The Public and Private CKDatabases each have a Default CKRecordZone.

New, custom CKRecordZones can be created in the Private and Shared databases (but not in the Public database).

A CKDatabase lives in a CKContainer. There is typically only one CKContainer for the whole app.

Summary of Concepts

This shows how these concepts relate to each other:

Summary of CloudKit Entity Relationships

Creating a CloudKit Schema

At first glance, the data structure for a shopping list would be similar to the Book/Author example above, with a parent “Shopping List” record and a set of child “ShoppingItem” records:

Alternative, State-based shoppping list schema

This is the way most tutorials approach the problem, and this is similar to the way Shopping UK stores data internally, but it isn’t the optimal structure for sharing a set of items on a list.

It is not enough to represent only the current state of the list. We need to see some history too, so we can answer questions like:

  • I added ‘milk’ yesterday, but it has disappeared. Is there a glitch in the app or did my wife remove it from the list?
  • I added ‘bread’ last week, but it is still on the list. Did my wife buy some and have we run out already?
  • When did we last buy coffee?

So instead of transferring a snapshot of the current list, Shopping UK transfers the ordered list of changes that have been applied to the list.

Real journal-based shoppping list schema

This “journal-based” approach is similar to how a relational database works internally (see write-ahead logging). It is a well-established pattern, but it isn’t appropriate for all apps. If you have an app that doesn’t need to track every change, a simple state-based sharing model, like the one shown earlier, would probably be simpler to implement.

When all changes have been applied to another device, the lists will become the same.

The journal-based approach has a couple of useful consequences:

  • Items don’t have an absolute position in the list. Instead, they know which item they follow. This means if my wife and I are both adding items to the list we won’t overwrite each other’s changes. Instead, each item will be added in turn.

  • Changes can be queued rather than being applied immediately. This is important when the app is in “Shopping” mode. It would be annoying if the app added an item to my checklist while I was shopping in the supermarket. It could cause me to mark-off the wrong item. Instead, the app will show the newly added items as notifications, and I can tap on them when I’m ready to include them in my checklist:

Queued Changes on Shopping List screen

You’re probably wondering what happens after the list has been shared for several weeks and the journal contains thousands of changes. We’ll look at how the journal is compressed in a later post.

Next week, we’ll look at the lifecycle of a CKRecord: how they are created, read, modified and deleted.


If you get a chance, please try Shopping UK and let me know what you think at @wheelies