Back in 2012, Apple introduced iCloud alongside iOS 5. At the same time, the company announced that developers would have access to iCloud through a number of APIs. At first, developers had three options:

These APIs aren't perfect, though. A major shortcoming is their lack of transparency. Core Data integration, in particular, has led to frustration and confusion among even the most experienced developers. When something goes wrong, developers have no idea what or who the culprit is. It could be a problem in their code or in Apple's.

CloudKit

At WWDC 2014, Apple introduced CloudKit, a brand new framework that directly interacts with Apple's iCloud servers. The framework is comparable to a number of PaaS (Platform as a Service) solutions, such as Firebase. Like Firebase, Apple provides a flexible API and a dashboard that offers developers a peek into the data stored on Apple's iCloud servers.

What I like most about CloudKit is Apple's own commitment to the framework. According to the company, iCloud Drive and iCloud Photo Library are built on top of CloudKit. This shows that the CloudKit framework and its infrastructure are robust and reliable.

As a developer, this sign of trust and commitment is important. In the past, Apple occasionally released APIs that were plagued by bugs or lacking key features simply because the company wasn't eating its own dog food. This isn't true for CloudKit. And that is promising.

Should You Use CloudKit?

Key-value storage and document storage have their uses, and Apple emphasizes that CloudKit doesn't replace or deprecate existing iCloud APIs. The same is true for Core Data. CloudKit doesn't offer local storage, for example. This means that an application running on a device without a network connection is pretty much useless if it solely relies on CloudKit.

Apple also emphasizes that error handling is critical when working with CloudKit. If a save operation fails, for example, and the user isn't notified, then she may not even know that her data wasn't saved—and lost.

CloudKit is a great solution for storing structured as well as non-structured data in the cloud. If you need a solution to access data on multiple devices, then CloudKit is certainly an option to consider.

At the 2015 WWDC, Apple did what few developers had expected or hoped for. It announced a web service for CloudKit. This means that CloudKit can be used on virtually any platform, including Android and Windows Phone.

Apple's pricing is pretty competitive too. Getting started with CloudKit is free, and it remains free for most applications. Again, CloudKit is certainly worth considering if you plan to store data in the cloud.

CloudKit Concepts

Developers struggling with Core Data are often unfamiliar with the building blocks of the framework. If you don't take the time to learn about and understand the Core Data stack, then you will inevitably run into problems. The same is true for CloudKit.

Before we start working on a sample application that uses CloudKit, I want to spend a few minutes introducing you to a number of key concepts of the CloudKit framework and infrastructure. Let's start with containers, databases, and sandboxing.

Privacy and Containment

Apple makes it very clear that privacy is an important aspect of CloudKit. The first thing to know is that each application has its own container in iCloud. This concept is very similar to how iOS applications each have their own sandbox. However, it is possible to share a container with other applications as long as those applications are associated with the same developer account. As you can imagine, this opens up a number of interesting possibilities for developers.

A CloudKit container contains several databases. Each container has one public database that can be used to store data that is accessible to every user of your application. In addition to the public database, a container also contains a private database for each user of your application. The user's private database is used to store data that is specific to that particular user. Data segregation and encapsulation is a key component of the CloudKit and iCloud infrastructure.

Even though an application's container can hold many databases, from a developer's perspective a container holds only two databases: the public database and the private database of the user that is currently signed in to their iCloud account. As of 2017, Apple introduced a third database, shared database, providing apps with the ability to share a subset of records that is shared on another user's private database, inviting them to contribute to those exposed records.

I'll talk more about iCloud accounts a bit later.

Records and Record Zones

The databases of an application's container store records. This isn't very different from a traditional database. At first glance, the records stored in a CloudKit database seem to be nothing more than wrappers for a dictionary of key-value pairs. They may look like glorified dictionaries, but that's only part of the story.

Each record also has a record type and a number of metadata fields. A record's metadata keeps track of when the record was created, which user created the record, when the record was last updated, and who updated the record.

The CKRecord class represents such a record, and it's a pretty powerful class. The values you can store in a record aren't limited to property list types. You can store strings, numbers, dates, and blobs of data in a record, but the CKRecord class also treats location data, CLLocation, as a first-class data type.

You can even store arrays of the supported data types in a record. In other words, arrays of strings or numbers are no problem for a CKRecord instance.

Records are organized in record zones. A record zone groups related records. The public and private database each have a default record zone, but it is possible to create custom record zones if needed. Record zones are an advanced topic that we won't discuss in much detail in this series.

Relationships

Relationships between records are managed by instances of the CKReference class. Let's look at an example to better understand how exactly relationships work. The application we will create in this series will manage a number of shopping lists. Each list can have zero or more items in it. This means that each item needs to have a reference to the list it belongs to.

It's important to understand that the item keeps a reference to the list. While it is possible to create an array of CKReference instances for the items of a list, it is more convenient—and recommended—to keep the foreign key with the item, not the list. This is also what Apple recommends.

The way CloudKit manages relationships is fairly basic, but it does provide an option to automatically delete a record's children when the parent record is deleted. We'll take a closer look at relationships a bit later in this series.

Assets

I also would like to mention the CKAsset class. While it's possible to store blobs of data in a record, unstructured data (such as images, audio, and video) should be stored as CKAsset instances. A CKAsset instance is always associated with a record, and it corresponds to a file on disk. We won't be working with the CKAsset class in this series.

Authentication

I'm sure you agree that CloudKit looks quite appealing. There is, however, an important detail that we haven't discussed yet: authentication. Users authenticate themselves through their iCloud accounts. Users who aren't signed in to an iCloud account are unable to write data to iCloud.

While this is true for any of the iCloud APIs, keep in mind that applications that solely rely on CloudKit won't be very functional in that case. All the user can do is access the data in the public database, if permitted by the developer.

Reading Data

Users who aren't signed in to their iCloud accounts can still read data from the public database. It goes without saying that the private database is not accessible since iCloud doesn't know who is using the application.

Reading and Writing

When signed in, users can read and write to the public and their private database. I already mentioned that Apple takes privacy very seriously. As a result, the records stored in the private database are only accessible by the user. Even you, the developer, cannot see the data users have stored in their private databases. This is the downside of Apple managing your application's back end, but it's a definite win for the user.

Shopping List

The application we're about to build will manage your shopping lists. Each shopping list will have a name and zero or more items. After building the shopping list application, you should feel pretty comfortable using the CloudKit framework in a project of your own.

Prerequisites

For this tutorial, I will be using Xcode 9 and Swift 4. If you are using an older version of Xcode, then keep in mind that you are using a different version of the Swift programming language. This means that you will need to update the project's source code to satisfy the compiler. The changes are mostly minor, but it's important to be aware of this.

Because CloudKit is an advanced topic, I am going to assume that you are familiar with both Xcode and the Swift programming language. If you are new to iOS development, then I recommend reading an introductory tutorial first or taking one of our courses on Swift development:

Be sure to check those out if you are new to iOS development or the Swift language.

Project Setup

It's time to start writing some code. Launch Xcode and create a new project based on the Single View Application template.

Give your project a name and an organization identifier. The resulting bundle identifier will be used to create the identifier of your application's default container. That identifier needs to be unique across developer accounts as they share one global namespace. It's therefore important to follow Apple's advice and use reverse domain name notation.

Enabling iCloud

The next step is enabling iCloud and CloudKit. Select the project in the Project Navigator on the left and select the target for your application from the list of targets. Open the General tab and set Team to the correct team. To avoid any problems in the next step, verify that your developer account has the required permissions to create an App ID.

Next, open the Capabilities tab at the top and set the switch for iCloud to on. Xcode will need a moment to create an App ID on your behalf. It will also add the necessary entitlements to the App ID. If this doesn't work, then make sure the team is correctly set and you have the required permissions to create an App ID.

Enabling CloudKit is as simple as checking the checkbox labeled CloudKit. By default, your application will use the default container for your application. This container is automatically created for you when you enable CloudKit.

If your application needs access to a different container or to multiple containers, then check the checkbox labeled Specify custom containers and check the containers your application requires access to.

You may have noticed that Xcode has automatically linked your target to the CloudKit framework. This means that you're ready to start using CloudKit in your application.

Getting Your Feet Wet

In the next tutorial of this series, we will add the ability to add, edit, and remove shopping lists. To finish this tutorial, however, I'd like to get your feet wet by showing you how to interact with the CloudKit API. All we're going to do is fetch the record of the currently signed-in user.

Open ViewController.swift and add an import statement at the top to import the CloudKit framework.

import UIKit
import CloudKit

To fetch the user record, we first need to fetch the record's identifier. Let's see how this works. I've created a helper method, fetchUserRecordID, to contain the logic for fetching the user's record identifier. We invoke this method in the view controller's viewDidLoad method.

The implementation of fetchUserRecordID is a bit more interesting than viewDidLoad. We first fetch a reference to the application's default container by invoking defaultContainer on the CKContainer class. We then call fetchUserRecordIDWithCompletionHandler(_:) on defaultContainer. This method accepts a closure as its only argument.

The closure accepts two arguments: an optional CKRecordID instance and an optional NSError instance. If error is nil, we safely unwrap recordID.

It's important to emphasize that the closure will be called on a background thread. This means that you need to be careful when updating the user interface of your application from within a closure invoked by CloudKit. In fetchUserRecordID, for example, I explicitly call fetchUserRecord(_:) on the main thread.

In fetchUserRecord(_:), we fetch the user record by telling CloudKit which record we're interested in. Notice that we call fetchRecordWithID(_:completionHandler:) on the privateDatabase object, a property of the defaultContainer object.

The method accepts a CKRecordID instance and a completion handler. The latter accepts an optional CKRecord instance and an NSError instance. If we successfully fetched the user record, we print it to Xcode's Console.

When you run the app in Xcode, you will see in the console something similar to:

This should have given you a taste of the CloudKit framework. Its modern API is intuitive and easy to use. In the next tutorial, we will dig deeper into the possibilities of the CloudKit API.

You can clone the complete sample project, at GitHub (tag #introduction).

Conclusion

You should now have a proper understanding of the basics of the CloudKit framework. The remainder of this series will be focused on building the shopping list application. In the next tutorial, we will start by adding the ability to add, edit, and remove shopping lists.