Core Data with CloudKit: Exploring the CloudKit Dashboard

fatbobman ( 东坡肘子)
12 min readMar 17, 2023
Photo by Stephen Phillips — Hostreviews.co.uk on Unsplash

In this article, we will explore the CloudKit Dashboard.

Don’t miss out on the latest updates and excellent articles about Swift, SwiftUI, Core Data, and SwiftData. Subscribe to fatbobman’s Swift Weekly and receive weekly insights and valuable content directly to your inbox.

In light of the fact that my blog, Fatbobman’s Blog, now offers all articles in English, starting from April 1, 2024, I will no longer continue updating articles on Medium. You are cordially invited to visit my blog for more content.

Introduction to the Dashboard

To use the CloudKit Dashboard, developers need to have an Apple Developer Program account and can access it at https://icloud.developer.apple.com.

In the past two years, Apple has made significant adjustments to the layout of the CloudKit Dashboard. The screenshot above shows what it looked like in mid-2021.

The original article was written in Chinese and published on my blog 肘子的Swift记事本.

Part 1: The Basics

Part 2: Syncing Local Database to iCloud Private Database

Part3: Exploring the CloudKit Dashboard

Part4: Troubleshooting

Part5: Synchronizing Public Database

Part6: Sharing Data in the iCloud

The Dashboard consists of three main sections:

Database (CloudKit Database)

A web client for the database. It covers features such as managing schemas, records, zones, user permissions, and container environments.

Telemetry (Telemetry)

Uses intuitive visual effects to gain insight into server-side performance of the application, as well as the utilization of cross-database and push events.

Logs (Logs)

CloudKit servers generate real-time and historical logs, recording and displaying interactions between the application and server.

In most scenarios that use Core Data with CloudKit, we only need to use a few features of the Dashboard (environment deployment). However, by utilizing the CloudKit Dashboard, we can gain a clearer understanding of some of the mechanisms behind the operation of Core Data data synchronization.

Database (CloudKit Database)

In The Basics, we have briefly introduced basic objects such as CKContainer, CKDatabase, CKZone, CKSubscription, and CKRecord. In this article, we will also introduce some other objects and features of CloudKit.

Environment

CloudKit provides separate development and production environments for your application's network data.

Development Environment

When your project is still in development, all data generated through CloudKit is only saved in the development environment, and only members of the development team can access the data in this environment. In the development environment, you can adjust the Schema structure and delete or modify the properties of Record Types at any time. Even if these operations may cause data conflicts between different versions, it is not a problem as the development environment can be reset anytime. This is very similar to the state of an application using Core Data before it goes live; even if data cannot be migrated correctly, just deleting and reinstalling the app will suffice. Through the development environment, developers can thoroughly test the application before providing CloudKit services to users.

Production Environment

When the application is developed and ready to be submitted to the App Store, the structure of the development environment needs to be deployed to the production environment (Deploy Schema Changes). Once the Schema is deployed to the production environment, developers cannot modify the Schema as freely as they can in the development environment. All modifications must be made in a forward-compatible way.

The reason is straightforward. Once the application goes live, we cannot control the update frequency of the client, which means that the client may have any structure version. In order to allow low-version clients to access the data, any changes to the data model need to be backward-compatible.

Applications sold on the App Store can only access the production environment.

Even if the developer’s developer account is the same as their personal iCloud account, the development and production environments are two separate sandboxes, and data does not affect each other. When debugging the program using Xcode, the application can only access the development environment, while the application downloaded through Testflight or the App Store can only access the production environment.

In the development environment, click on Deploy Schema Changes to deploy the Schema of the development environment to the production environment.

During deployment, modifications made in the development environment since the last deployment will be displayed.

Even if the Schema has been deployed to the production environment, we can still make changes in the development environment and deploy them to the production environment. If the model does not meet compatibility requirements, the CloudKit dashboard will prohibit your deployment.

Whether the Schema has been deployed to the production environment is shown below the container name. The above picture shows the undeployed state, while the following picture shows the deployed state.

Before performing any operation, be sure to confirm that you are in the correct environment setting.

Given the deployment rules of CloudKit environments, when designing a Core Data data model in a project that uses Core Data with CloudKit, you must be particularly careful! My personal principle is to add and subtract, and avoid changing as much as possible. I will discuss in detail how to migrate versions of Core Data with CloudKit data models in the next article.

Security Roles

Security roles are only applicable to public databases.

CloudKit uses role-based access control (RBAC) to manage permissions and control access to data in public databases (private databases are exclusive to the application's users). With CloudKit, you can set permission levels for a role and then assign that role to a given record type.

Permissions include read, write, and create. Read permission only allows reading records, write permission allows reading and writing records, and create permission allows reading, writing, and creating new records.

CloudKit includes 3 preset roles, which are World (_world), Authenticated (_icloud), and Creator (_creator). World represents anyone, whether or not they are an iCloud user. Authenticated applies to any verified iCloud user. Creator is the creator of the record.

By default, anyone can read data, only verified iCloud users can create new records, and record creators can update their own records.

We can create custom security roles, but we cannot create user records. The system will create a user record for the user when they first authenticate with the container. We can look up existing users and assign them to any custom roles.

Security roles are part of the data schema, and whenever a developer modifies security settings, they need to deploy it to the production environment for it to take effect. Security roles cannot be deleted after deployment.

For most Core Data with CloudKit applications, using the default configuration provided by the system is sufficient.

Indexes

CloudKit indexes are divided into three types:

  • Queryable
  • Searchable
  • Sortable

After creating a Record Type through CloudKit, we can create the required indexes for each field as needed (only NSString supports searchable). Index type options are independent, so if you want a field to be both queryable and sortable, you need to create two indexes separately.

Only after creating a queryable index for the recordName of the Record Type can you browse the data for that type in Records.

Core Data with CloudKit automatically creates the required indexes on CloudKit for each attribute of the Core Data data model (excluding recordName). We don't need to add any indexes unless we need to browse data on the CloudKit dashboard.

Record Types

A Record Type is a type identifier specified by developers for CKRecord. You can create it directly in code or create and modify it in the CloudKit dashboard.

In the basics section, we mentioned that an Entity has more configuration information than a Record Type. However, Record Type has a feature that Entity does not have - metadata.

CloudKit has several pre-set metadata fields for each Record Type (even if the developer has not created any other fields), and each data record (CKRecord) will contain this information, most of which are automatically set by the system.

createdTimestamp

The time when CloudKit first saved the record to the server.

createUserRecordName

The user record of _creator, which is saved in Users (created by the system). The system creates a user record for the user when they first authenticate with the container.

_etag

Version token. Each time CloudKit saves a record, it updates it to a new value. Used to compare the version of network and local data.

modifiedTimestamp

The most recent time when CloudKit updated the record.

modifiedUserRecordName

The user record of the user who last updated the data.

recordName

The unique ID of the record. Created when creating CKRcord, usually set to a UUID string.

For some special types of Record Type, the system will also add some targeted metadata, such as role,cloud.shared, etc.

The main topic of this article is Core Data with CloudKit, so let's take a look at how NSPersistentCloudKitContainer converts the properties of Core Data managed objects into Record Type fields in CloudKit.

The above figures show the Record Type corresponding to the Item template project in CloudKit in Syncing Local Database to iCloud Private Database. CloudKit automatically creates a field for each property of the managed object entity, mapping the property name to a field with the key CD_[attribute.name]. The type of this field may also differ between Core Data and CloudKit. The name of the Record Type is CD_[entity]. All operations are automatically performed by the system, and no intervention is required. In addition, a CD_entityName field is generated for the Enitity, which contains the class mapping name of the Entity.

These strings with the prefix CD_ will appear repeatedly in the console during data synchronization, and understanding their composition is helpful for debugging the code.

After deploying the Record Type to the production environment, fields cannot be deleted, and field names cannot be changed. Therefore, some operations in Core Data are not allowed in `Core Data with CloudKit`.

Please do not rename the Entity or Attribute of the data model of an application that has already been deployed, even if you use Mapping Model or Renaming ID. If renaming is required during the development phase, you may need to delete the app, reinstall it, and reset the development environment of CloudKit.

Even if you have deleted an Attribute of an Entity in Core Data, the field will still exist in the Record Type (and will not affect synchronization).

Zones

Each type of database has a default Zone, and only private databases can customize their Zone.

For data in a private database, you can specify a Zone for the data when creating a CKRecord.

let zone = CKRecordZone(zoneName: "myZone")
let newStudent = CKRecord(recordType: "Student",
recordID: CKRecord.ID(recordName: UUID().uuidString,
zoneID: zone.zoneID))s

When NSPersistentCloudKitContainer converts managed objects to CKRecord, it sets the ZoneID to com.apple.coredata.cloudkit.zone by default. You must switch to the correct Zone to view the data.

OWNER RECORD NAME

User record, corresponding to _creator of the Zone

CHANGE TOKEN

Comparison token

ATOMIC

If set to true, the entire operation fails when CloudKit fails to update one or more records in a Zone.

Records

Used for browsing, creating, deleting, modifying, and querying data records.

When browsing data, please pay attention to the following points:

  • Select the correct environment (the data in the development environment and the production environment are completely different).
  • Select the correct Database and Zone.
  • Confirm that the metadata recordName of the Record Type you want to browse has already added a queryable index.
  • If you need to sort or filter fields, create an index for the field.
  • Indexes only take effect in the production environment after deployment.

Modifying the mirrored data of Core Data in the CloudKit dashboard will immediately trigger a remote notification and update on the client side. However, this approach is not recommended.

You can also get the CKRecord corresponding to the Core Data managed object:

func getLastUserID(_ object:Item?) -> CKRecord.ID? {
guard let item = object else {return nil}
guard let ckRecord = PersistenceController.shared.container.record(for: item.objectID) else {return nil}
guard let userID = ckRecord.lastModifiedUserRecordID else {
print("can't get userID")
return nil
}
return userID
}

The above code will obtain the last modified user for the CKRecord corresponding to the managed object record.

Subscriptions

Browse the CKSubscriptions registered on the container.

CKSubscriptions are created through code and can only be viewed or deleted on the dashboard.

For example, the following code creates a CKQuerySubscription:

let predicate = NSPredicate(format: "name = 'bob'")
let subscription = CKQuerySubscription(recordType: "Student",
predicate: predicate,
options: [.firesOnRecordCreation])
let info = CKSubscription.NotificationInfo()
info.alertLocalizationKey = "create a new bob"
info.soundName = "NewAlert.aiff"
info.shouldBadge = true
info.alertBody = "hello world"

subscription.notificationInfo = info

publicDB.save(subscription) { subscription, error in
if let error = error {
print("error:\(error)")
}
guard let subscription = subscription else { return }
print("save subscription successes:\(subscription)")
}

NSPersistentCloudKitContainer registers a CKDatabaseSubscription for the privately mirrored database of Core Data. When the com.apple.coredata.cloudkit.zone data is updated, a remote notification will be pushed.

Tokens&Keys

Set the API token for the container.

In addition to operating data through code and the CloudKit dashboard, Apple also provides means to access iCloud data from the internet or other platforms. After obtaining the token, developers can interact with data using CloudKit JS or CloudKit Web Services.

Some developers have used these services to develop third-party libraries that can access iCloud data on other platforms, such as DroidNubeKit (accessing CloudKit on Android).

For network mirrored data in Core Data, it is not recommended to attempt this unless your data model is simple enough. CloudKit Web Services are more suitable for data records created directly through Cloudkit.

Sharing Fallbackd

Sharing Fallbackd provides data recording and sharing callback support for operating systems below iOS 10 and macOS Sierra.

Telemetry

By viewing Telemetry metrics, you can visualize performance while developing or updating your application. This includes the number of requests, errors, pushes, server latency, and average request size, among others. By setting the range, only data relevant to you is displayed, helping you better understand traffic configuration and usage trends for your application.

Logs

In the historical logs, you can view information such as time, client platform version, user (anonymous), event, organization, and details.

While providing detailed information, CloudKit strives to maintain user data privacy. The logs display server events recorded for each user, but do not expose any personal identity information. Only anonymous, container-specific CloudKit users are displayed.

App Store Connect’s analytics information only comes from users who have agreed to share diagnostic and usage information with app developers, while CloudKit log information comes from all users who have used CloudKit services in your application. Using both together can yield better results.

Conclusion

For most scenarios using Core Data with CloudKit, developers generally do not need to use the CloudKit dashboard. However, occasionally studying the data on the dashboard can also be a good source of fun.

In the next article, we will discuss some situations that developers often encounter when developing Core Data with CloudKit projects, such as debugging, testing, and data migration.

If you found this article helpful or enjoyed reading it, consider making a donation to support my writing. Your contribution will help me continue creating valuable content for you.
Donate via Patreon, Buy Me aCoffee or PayPal.

Want to Connect?

@fatbobman on Twitter.

--

--

fatbobman ( 东坡肘子)

Blogger | Sharing articles at https://fatbobman.com | Publisher of a weekly newsletter on Swift at http://https://weekly.fatbobman.com