Article

Creating a Core Data Model for CloudKit

Design a CloudKit-compatible data model and initialize your CloudKit schema.

Overview

To pass records back and forth, Core Data stores and the CloudKit database need a shared understanding of the data’s structure. You define this information first in the Core Data model, then use it to generate a schema for CloudKit.

Flow diagram showing that your app’s Core Data model generates a CloudKit schema.

Design a CloudKit-Compatible Core Data Model

Use the Core Data model editor to define your app’s entities and their properties, or construct your model in code. See Creating a Core Data Model and the articles under Modeling Data.

The CloudKit schema doesn’t support all Core Data model features. As you design your model, note the following limits of the CloudKit schema and make sure you design a compatible model.

Core Data Model element

CloudKit Schema limitation

Entities

Unique constraints aren’t supported.

Attributes

Undefined and objectID attribute types aren’t supported.

Relationships

All relationships must be optional. Due to operation size limitations, relationship changes may not be saved atomically.

All relationships must have an inverse, in case the records synchronize out of order. (In a Core Data model, an inverse is strongly recommended but not required.)

The Deny deletion rule is not supported.

Configurations

Entities in a configuration must not have relationships to entities in another configuration.

For more information about how Core Data translates managed objects to CloudKit records, see Reading CloudKit Records for Core Data.

Initialize Your CloudKit Schema During Development

Once you’ve created your Core Data model, you tell CloudKit what types of records CloudKit needs to store by initializing a development schema. The development schema is a draft that you can rewrite as often as needed during development, before the record types and attributes become immutable in production.

Initialize the schema from your developer tooling, for example, from an integration test. If you like, you can initialize the schema as part of every test run.

In the code that sets up your NSPersistentCloudKitContainer, initialize an NSPersistentCloudKitContainerOptions on the container. Toggle the shouldInitializeSchema property on the container options to initialize the schema.

  1. Set shouldInitializeSchema to true.

  2. Run your app on a device. Look for this log line in your console output: <NSCloudKitMirroringDelegate: 0x7f9699d29a90>: Successfully set up CloudKit integration for store

  3. Set shouldInitializeSchema to false.

let container = NSPersistentCloudKitContainer(name: "Earthquakes")

// get the store description
guard let description = container.persistentStoreDescriptions.first else {
    fatalError("Could not retrieve a persistent store description.")
}

// initialize the CloudKit schema
let id = "iCloud.com.example.apple-samplecode.Earthquakes"
let options = NSPersistentCloudKitContainerOptions(containerIdentifier: id)
options.shouldInitializeSchema = true // toggle to false when done
description.cloudKitContainerOptions = options

During initialization, Core Data creates representative CKRecord instances for all stores in the container that use Core Data with CloudKit and uploads them to CloudKit. Finally, the schema becomes visible in the CloudKit dashboard, and Core Data deletes the representative records.

Reset shouldInitializeSchema after initialization to ensure that you only create these records once.

For more information about initializing an NSPersistentCloudKitContainer, see Setting Up Core Data with CloudKit.

Throughout Development, Reset the Environment

As you change the model during development, periodically visit the CloudKit dashboard to reset the development environment and delete the existing development schema, before initializing a new one. For more information about resetting the development environment, see Using CloudKit Dashboard to Manage Databases.

Push Your Schema from Development to Production

When you’re satisfied with your model, have tested your app, and are ready to submit it to the app store, it’s time to promote your schema from development to production.

Initialize your schema one last time, then promote it from the CloudKit dashboard. For more information about promoting a schema from development to production, see Deploying the Schema.

Update Your Production Schema

Plan carefully how your app will handle forward compatibility and major changes to the model, because you can’t rename or delete CloudKit record types and fields in production. Consider these strategies:

  • Migrate users to a completely new store, using NSPersistentCloudKitContainerOptions to associate the new store with a new container.

  • Incrementally add new fields on existing record types. However, earlier versions of the app can’t read these new fields.

  • Version your entities by including a version attribute from the start. Use a fetch request with a predicate to direct different versions of the app to access the records they support. Older versions of the app ignore new data.

For example, consider a Post entity with a version attribute. In earlier app versions, fetch only those records created with the entity’s original fields. For later app versions, fetch both original records and records that contain added fields.

let context = NSManagedObjectContext(
    concurrencyType: .privateQueueConcurrencyType
)
context.performAndWait {
    // select a version of the entity to load
    let maxAllowedVersion = 1 // for earlier versions of the app
    let laterMaxAllowedVersion = 3 // for later versions of the app
    let version = maxAllowedVersion
    
    // fetch records up to and including the supported version
    let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Post")
    fetchRequest.predicate = NSPredicate(
        format: "version <= %d",
        argumentArray: [version]
    )
    let results = context.fetch(fetchRequest) //posts <= 1, or posts <= 3
}

Along with these choices, consider your timelines for supporting multiple versions of your app, and for migrating users to newer app versions.

For tips on migrating records to a new schema, see Designing for CloudKit.

For more information about Core Data model migration, see Using Lightweight Migration. For heavyweight migration, see the Core Data Model Versioning and Data Migration guide.

See Also

Configuring CloudKit Mirroring

Setting Up Core Data with CloudKit

Set up the classes and capabilities that sync your store to CloudKit.

Syncing a Core Data Store with CloudKit

Synchronize objects between devices, and handle store changes in the user interface.

Reading CloudKit Records for Core Data

Access CloudKit records created from Core Data managed objects.