CloudKit Support & Extending SwiftData Apps Introduction

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Your app is coming along nicely. With SwiftData, you’ve been able to create dog records, update the metadata about them, and keep track of their favorite parks. With PhotoKit, you’ve also been able to attach an image to the dogs. Adding CloudKit to the app will make for a more compelling capability to sync the dog records between the user’s iPhone, iPad, Vision Pro, and Mac.

Note: Enabling CloudKit along with other advanced Capabilities requires a paid Apple Developer Account. If you don’t have access to one or not a member in one, you can follow along with this lesson however you won’t be able to enable the iCloud and Remote Notifications in the app.

Adding CloudKit to a modern app is surprisingly simple these days. Trying to get a handle on what’s needed to configure and sync the data objects used to require quite a learning curve and a lot of code. There were a lot of moving parts, like NSPersistentCloudKitContainers, creating and receiving notifications, and understanding CKRecord formats. It was similar to learning and understanding the roles of NSPersistentStoreCoordinator, NSManagedObjectContext, and NSManagedObjectModel. Those terms may sound familiar as they’re part of the Core Data framework. SwiftData uses terms like model, modelContext, and persistent store by comparison, making it simpler and easier to use. You should be quite familiar with these terms now.

To configure CloudKit in an app, you need to follow a few steps. Start by selecting your app’s target. Enable Automatic Signing and select your team. Next, go to the Signing & Capabilities tab and click + Capabilities. Locate iCloud and check the box next to CloudKit. Next, click the + in Containers section and create a name for your app container.

The standard naming is iCloud.com.yourdomain.yourapp. For example, iCloud.com.kodeco.GoodDog. Using your reverse domain name ensures that your CloudKit Container name is unique. Once you have these configured, your app user’s data will travel up from one device to CloudKit and then travel down to another device owned by the user. The app sends and receives notifications to and from CloudKit.

For your app to send and receive notifications, check the box and add Remote Notifications from the Background Modes section in Signing & Capabilities. With that service in place, a notification is sent to CloudKit when data is changed by the modelContext. Instances of your application also listen for these notifications. Behind the scenes, your app’s SwiftData ManagedObjects are converted to CKRecords, which are inserted or updated in your app’s CloudKit Container.

There are two types of data stores, or Zones, in CloudKit: .private and .shared. The Shared type is used for frameworks like TipKit to track tips shared with other users. SwiftData currently only has access to the private zones. The data that’s synced is only available to one user signed in with one Apple ID.

In addition to the straightforward setup, you can view your CloudKit Container with Apple’s CloudKit Console in a web browser. Log in with your developer account, and you’ll see your team’s containers listed at the top left.

The screenshot depicts the dashboard. Under the Records section, you select Private Database and the com.apple.coredata.cloudkit.zone. Then, you log in as another iCloud Account user, like a sandbox account, with their Apple ID credentials.

You then see a table with their CKRecords. A clickable identifier on the left column opens the Record Details on the right in an editable form. Notice in the screenshot that each field name represents your DogModel’s properties, each with a CD_ prefix. This stands for Core Data, which is the base of the SwiftData framework.

The CloudKit Console is a great resource for inspecting your app’s data objects. This year, the console has added Notifications, which you can see by clicking the bell icon at the top right. You can set up notifications by email or text when an event you’re interested in occurs.

Also new are Telemetry, Alerts, and Logs. Telemetry can show you the data on your app’s requests, errors, latency, and bandwidth. You can also filter by platform to narrow down the statistics.

Logs give you access to individual events. You can download them in JSON or CSV for further diagnosis.

Notifications, Telemetry, Alerts, and Logs are beyond the scope of this course. They’re presented here for your information. If you’d like more information, check out this WWDC24 video. Use CloudKit Console to monitor and optimize database activity.

Setting Up CloudKit Indexes

Before you can query the data in CloudKit Console, you need to configure the indexing. If you’ve selected Private Database and you only see __defaultZone, click Zones on the left sidebar and choose Fetch Zones. When you try to Query records for the first time, after you set Private Database and select the com.apple.coredata.cloudkit.zone under the Records, you may see an error -  ‘Field recordName isn’t marked queryable.’ This means you need to set the recordName index to Queryable.

Concurrency

After you have set up CloudKit, you’ll also see a demo of using concurrency when dealing with large transactions. Core Data was known for not being thread-safe. This could block the UI and cause data corruption. This imposed limits on how Core Data was used, and crafty developers came up with many workarounds.

Async/Await

SwiftData is built to use Swift Concurrency to handle multiple operations. You would have noticed already that there have been many functions with the try keyword throughout the course. In the lesson, you’ll learn how to use a Task with async/await to optimize a large function. The task tells the app where to perform the work on a concurrent thread, and is marked with await to ensure that tasks are completed in order.

ModelActor

SwiftData also introduces a new Swift macro, @ModelActor, to be able to handle tasks on a serial thread. It’s ideal for dealing with a large amount of SwiftData operations. The ModelActor takes in a modelContainer, maintains UI responsiveness and prevents data races. Try this out and see how smooth the ModelActor is.

See forum comments
Download course materials from Github
Previous: CloudKit Support & Extending SwiftData Apps Next: CloudKit Support & Extending SwiftData Apps Demo