CloudKit provides a way for apps to store cloud-based databases using iCloud storage so that it is accessible across multiple devices, users, and apps.
Although initially provided with a dedicated framework that allows code to be written to directly create, manage and access iCloud-based databases, the recommended approach is now to use CloudKit in conjunction with Core Data.
This chapter will provide a high-level introduction to the various elements that make up CloudKit, and explain how those correspond to Core Data.
An Overview of CloudKit
The CloudKit Framework provides applications with access to the iCloud servers hosted by Apple and provides an easy-to-use way to store, manage and retrieve data and other asset types (such as large binary files, videos, and images) in a structured way. This provides a platform for users to store private data and access it from multiple devices, and also for the developer to provide data that is publicly available to all the users of an application.
The first step in learning to use CloudKit is to gain an understanding of the key components that constitute the CloudKit framework. Keep in mind that we won’t be directly working with these components when using Core Data with CloudKit. We will, instead, continue to work with the Core Data elements covered in the previous chapters using a CloudKit-enabled version of the Persistent Container. This container will handle all of the work of mapping these Core Data components to their equivalents within the CloudKit ecosystem.
While it is theoretically possible to implement CloudKit-based Core Data storage without this knowledge, this information will be useful when using the CloudKit Console. Basic knowledge of how CloudKit works will also be invaluable if you decide to explore more advanced topics in the future such as CloudKit sharing and subscriptions.
Each CloudKit-enabled application has at least one container on iCloud. The container for an application is represented in CloudKit by the CKContainer class and it is within these containers that the databases reside. Containers may also be shared between multiple applications. When working with Core Data, the container can be thought of as the equivalent of the Managed Object Model.
CloudKit Public Database
Each cloud container contains a single public database. This is the database into which is stored data that is needed by all users of an application. A map application, for example, might have a set of data about locations and routes that apply to all users of the application. This data would be stored within the public database of the application’s cloud container.
CloudKit Private Databases
Private cloud databases are used to store data that is private to each specific user. Each cloud container, therefore, will contain one private database for each user of the application.
Data Storage Quotas
Data and assets stored in the public cloud database of an app count against the storage quota of the app. Anything stored in a private database, on the other hand, is counted against the iCloud quota of the corresponding user. Applications should, therefore, try to minimize the amount of data stored in private databases to avoid users having to unnecessarily purchase additional iCloud storage space.
At the time of writing, each application is provided with 1PB of free iCloud storage for public data for all of its users.
Apple also imposes limits on the volume of data transfers and the number of queries per second that are included in the free tier. While official documentation on these quotas and corresponding pricing is hard to find, it is unlikely that the average project will encounter these restrictions.
Data is stored in both the public and private databases in the form of records. Records are represented by the CKRecord class and are essentially dictionaries of key-value pairs where keys are used to reference the data values stored in the record. When stored is data via CloudKit using Core Data, these records are represented by Core Data Managed Objects.
The overall concept of an application cloud container, private and public databases, zones, and records can be visualized as illustrated in Figure 45-1:
CloudKit Record IDs
Each CloudKit record has associated with it a unique record ID represented by the CKRecordID class. If a record ID is not specified when a record is first created, one is provided for it automatically by the CloudKit framework.
CloudKit references are implemented using the CKReference class and provide a way to establish relationships between different records in a database. A reference is established by creating a CKReference instance for an originating record and assigning to it the record to which the relationship is to be targeted. The CKReference object is then stored in the originating record as a key-value pair field. A single record can contain multiple references to other records.
Once a record is configured with a reference pointing to a target record, that record is said to be owned by the target record. When the owner record is deleted, all records that refer to it are also deleted and so on down the chain of references (a concept referred to as cascading deletes).
CloudKit record zones (CKRecordZone) provide a mechanism for relating groups of records within a private database. Unless a record zone is specified when a record is saved to the cloud it is placed in the default zone of the target database. Custom zones can be added to private databases and used to organize related records and perform tasks such as writing to multiple records simultaneously in a single transaction. Each record zone has associated with it a unique record zone ID (CKRecordZoneID) which must be referenced when adding new records to a zone. All of the records within a public database are considered to be in the public default zone.
The CloudKit record zone translates to the Core Data persistent container. When working with Core Data in the previous chapter, persistent containers were created as instances of the NSPersistentContainer class. When integrating Core Data with CloudKit, however, we will be using the NSPersistentCloudKitContainer class instead. In terms of modifying code to use Core Data with CloudKit, this usually simply involves substituting NSPersistentCloudKitContainer for NSPersistentContainer.
The CloudKit Console is a web-based portal that provides an interface for managing the CloudKit options and storage for applications. The console can be accessed via the following URL:
Alternatively, the CloudKit Console can be accessed via the button located in the iCloud section of the Xcode Signing & Capabilities panel for a project as shown in Figure 45-2:
Access to the dashboard requires a valid Apple developer login and password and, once loaded into a browser window, will appear providing access to the CloudKit containers associated with your team account.
Once one or more containers have been created, the console provides the ability to view data, add, update, query, and delete records, modify the database schema, view subscriptions and configure new security roles. It also provides an interface for migrating data from a development environment over to a production environment in preparation for an application to go live in the App Store
The Logs and Telemetry options provide an overview of CloudKit usage by the currently selected container, including operations performed per second, average data request size and error frequency, and log details of each transaction.
In the case of data access through the CloudKit Console, it is important to be aware that private user data cannot be accessed using the dashboard interface. Only data stored in the public database and the private databases belonging to the developer account used to log in to the console can be viewed and modified.
Clearly, a CloudKit record contained within the public database of an app is accessible to all users of that app. Situations might arise, however, where a user wants to share with others specific records contained within a private database. This was made possible with the introduction of CloudKit sharing.
CloudKit subscriptions allow users to be notified when a change occurs within the cloud databases belonging to an installed app. Subscriptions use the standard iOS push notifications infrastructure and can be triggered based on a variety of criteria such as when records are added, updated, or deleted. Notifications can also be further refined using predicates so that notifications are based on data in a record matching certain criteria. When a notification arrives, it is presented to the user in the same way as other notifications through an alert or a notification entry on the lock screen.
This chapter has covered a number of the key classes and elements that make up the data storage features of the CloudKit framework. Each application has its own cloud container which, in turn, contains a single public cloud database in addition to one private database for each application user. Data is stored in databases in the form of records using key-value pair fields. Larger data such as videos and photos are stored as assets which, in turn, are stored as fields in records. Records stored in private databases can be grouped into record zones and records may be associated with each other through the creation of relationships. Each application user has an iCloud user id and a corresponding user record both of which can be obtained using the CloudKit framework. In addition, CloudKit user discovery can be used to obtain, subject to permission having been given, a list of IDs for those users in the current user’s address book who have also installed and run the app.
Finally, the CloudKit Dashboard is a web-based portal that provides an interface for managing the CloudKit options and storage for applications.