Now that we know how to setup a local Core Data stack, it’s time to learn how to save this data to the cloud. This post is an introduction on how to use CloudKit, and is by no means a comprehensive guide. At the end of this, I want you to be able to setup a basic Core Data stack that automatically syncs to the cloud.

Following along with this will create containers in your iCloud developer account. These containers are not deletable. This isn’t necessarily an issue, but it’s annoying to see a ton of containers.

Setting up Xcode

The first thing you’ll have to do is setup a developer account on Apple’s website. Without this, you cannot use CloudKit at all. You need to either have a personal account or a business account. Once you set this up, you have to sign into Xcode with this this account.

The next thing you need to do is sign into your Apple account in the simulator. Once you log into your account, CloudKit will work in the simulator. Data stored in the iCloud will be synced between simulators and your real devices.

Existing Resources

The first thing I do when learning something new is check out what documentation already exists for it. Apple has 2 pages I found extremely helpful, and I found a nice blog about this.

The first page links to an app Apple created using Core Data + CloudKit. This app isn’t SwiftUI, but it answered a lot of questions for me. Most of the important code is in the CoreDataStack.swift file.

Running this in the simulator revealed some weird quirks with CloudKit in the simulator. The way the app was setup, it synchronization doesn’t happen very often. I had to re-open the app before synchronization occurred.

The second page I found was a step by step guide to setting up Core Data with CloudKit in Swift. I followed everything up to the Manage Multiple Stores section. This pages gets you 90% of the way to getting CloudKit working.

The final page I found is a blog from Andrew Bancroft. His page was very helpful, but I noticed that some of the things he did didn’t seem to be necessary. If you haven’t checked out his blog before, I highly recommend it. He has a lot of good information on it.

Step 1: Setup Core Data with CloudKit

When you initialize a project with CloudKit, you’ll see some differences in the AppDelegate.swift file. The biggest change is that persistentContainer now returns a NSPersistentCloudKitContainer instead of a NSPersistentContainer. If you didn’t initialize the project with CloudKit, you’ll need to update this manually. This ends up looking like the following:

class AppDelegate: UIResponder, UIApplicationDelegate { // Other App Delegate methods lazy var persistentContainer: NSPersistentCloudKitContainer = { let container = NSPersistentCloudKitContainer(name: "YourAppName") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) return container }() }

We will need more changes in persistentContainer, but I’ll get to this in a later step.

Step 2: Setup your capabilities and container

The next thing we need to do is setup the iCloud capability. You can do this by going to your App settings, clicking on the +Capability button, and searching for iCloud. Once you add the iCloud capability, you need to check the CloudKit services box and create a new container.

You also need to add the Background Modes capability and check the Remote Notifications option. CloudKit uses notifications to update Core Data, so you need to enable them.

Adding a new capability will create a new entitlements file and add the container to it. This container will eventually show up in https://icloud.developer.apple.com. I haven’t been able to pin down exactly what creates this container, but I’m pretty sure they are created when you enter it in the config. Your entitlements file will look something like:

<plist version="1.0"> <dict> <key>aps-environment</key> <string>development</string> <key>com.apple.developer.icloud-container-identifiers</key> <array> <string>iCloud.com.example.test.6</string> </array> <key>com.apple.developer.icloud-services</key> <array> <string>CloudKit</string> </array> </dict> </plist>

Step 3: Setup your model

This next step deals with your .xcdatamodel file. The Apple tutorials say you need to edit your configurations and check the Used with CloudKit box. However, in my testing, this box does nothing.

The next thing you need to do is create an entity. I’ll just call my entity Entity and give it a single property name. I want to make my own class file for this entity, so I’ll change the class codegen to manual/none. If you don’t want to manually make your class, you can skip this step and go to the next section.

Finally, i’ll make my Entity class. I’ve seen a few different examples of what you need for this to work. From my testing, you just need a basic class like:

class Entity: NSManagedObject { @NSManaged var name: String? }

At this point, we have an app that will upload data to the iCloud. Now we need to pull changes from the iCloud.

Step 4: Automatically merge changes

Setting up merging is pretty simple. You have to set a flag, then choose how you want to merge the data using a merge policy.

Apple provides a bunch of merge policies. The default behavior is to throw an error, but you can also have local or remote changes be the source of truth. For now, I’m going to follow the demo and use NSMergeByPropertyStoreTrumpMergePolicy. The rest of them are:

NSMergeByPropertyStoreTrumpMergePolicy – The merge occurs by individual property. External changes trump the in-memory ones.

NSErrorMergePolicy – Default. In the case of failure, the save method returns with an error with a userInfo dictionary.

NSMergeByPropertyObjectTrumpMergePolicy – The merge occurs by individual property. In-memory changes trump the external ones.

NSOverwriteMergePolicy – Changed objects’ current state is forced upon the persistent store.

NSRollbackMergePolicy – The persistent store’s version of the objects’ state is used.

You also need to set automaticallyMergesChangesFromParent to true. Without this, changes will not be pulled down from the iCloud to your phone. All of these changes go in our AppDelegate.swift file. This ends up looking like:

class AppDelegate: UIResponder, UIApplicationDelegate { // Other App Delegate variables lazy var persistentContainer: NSPersistentCloudKitContainer = { let container = NSPersistentCloudKitContainer(name: "AppName") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy container.viewContext.automaticallyMergesChangesFromParent = true return container }() }

Step 5: Execute code on merge

In the demo app, Apple has code that runs whenever updates are received. They listen for NSPersistentStoreRemoteChange notifications in NotificationCenter. Their code didn’t work right off the bat for me, but updating the NSPersistentStoreRemoteChange Observer in their code to use nil instead of container got this to work.

The base code for doing this is something like:

lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentCloudKitContainer(name: "AppName") // Enable remote notifications guard let description = container.persistentStoreDescriptions.first else { fatalError("###\(#function): Failed to retrieve a persistent store description.") } description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy container.viewContext.automaticallyMergesChangesFromParent = true // Observe Core Data remote change notifications. NotificationCenter.default.addObserver( self, selector: #selector(self.someFunction), name: .NSPersistentStoreRemoteChange, object: nil) return container }()

The first step is setting NSPersistentStoreRemoteChangeNotificationPostOptionKey to true. This tells our persistent container to notify us when there are changes in the cloud that we don’t have.

The next step is to add an observer for NSPersistentStoreRemoteChange notifications. The selector you pass in here will get called whenever a change from the cloud is saved to your device.

What now?

At this point, you should be able to setup a Core Data stack with CloudKit. There’s a lot I didn’t cover in this article (like more advanced merging techniques), but I hope that you learned something from this.

Github repo to some code: https://github.com/SchwiftyUI/OrderedList

If you want to help support this blog: https://www.patreon.com/SchwiftyUI

Feel free to check out my YouTube video on this if you found this article interesting.

Thanks for reading.

– SchwiftyUI