To sync SwiftData through iCloud, add two capabilities to the app (iCloud with CloudKit and a container, and Background Modes with Remote notifications), make your models CloudKit-compatible, and run the app so the development schema gets created. Then deploy that schema to production in CloudKit Console before you ship, because App Store builds can only reach the production environment. Most of the work is in the models: no unique constraints, optional relationships, and values for every attribute. To check a model file against these rules in your browser, use our SwiftData and CloudKit checker.
Apple's article says SwiftData uses Core Data's NSPersistentCloudKitContainer to do the CloudKit synchronization, and that it reads your app's entitlements to work out the CloudKit configuration.
Add the capabilities
Apple lists two capabilities that SwiftData needs for iCloud sync:
- iCloud. Turn on CloudKit and create or choose a container, which Apple describes as the object CloudKit uses to isolate your app's databases on the iCloud servers. The capability needs an Apple Developer account with admin permissions.
- Background Modes. Turn on the Remote notifications option. The system delivers these notifications silently, and SwiftData uses them to learn about changes made on other devices and to apply them.
Choose a CloudKit database in ModelConfiguration
ModelConfiguration (iOS 17, macOS 14) takes a cloudKitDatabase argument of type ModelConfiguration.CloudKitDatabase. It has three values:
- The default. SwiftData reads your
Entitlements.plistand picks the first container identifier it finds. If you never pass the argument, this is what you get. .private("iCloud.com.example.Trips"). Use a specific container. Apple suggests this when the app has more than one CloudKit container..none. Turn off SwiftData's sync, overriding any container it would have found. Apple's use for it is an app that already uses CloudKit in its own way and whose schema may clash.
import SwiftUI
import SwiftData
@main
struct TripsApp: App {
let modelContainer: ModelContainer
init() {
let config = ModelConfiguration(cloudKitDatabase: .private("iCloud.com.example.Trips"))
do {
modelContainer = try ModelContainer(for: Trip.self, Stay.self, configurations: config)
} catch {
fatalError(error.localizedDescription)
}
}
var body: some Scene {
WindowGroup { Text("Trips") }
.modelContainer(modelContainer)
}
}
func localOnlyContainer() throws -> ModelContainer {
let config = ModelConfiguration(cloudKitDatabase: .none)
return try ModelContainer(for: Trip.self, Stay.self, configurations: config)
}
func defaultContainer() throws -> ModelContainer {
try ModelContainer(for: Trip.self, Stay.self)
}
One caution from Apple: if your app already has a production CloudKit schema, name only containers that SwiftData or Core Data managed before. Other containers are incompatible.
Shape the models for CloudKit
SwiftData's macros often produce a CloudKit-compatible schema already, but a few features do not carry over. This table pairs Apple's list with one extra rule.
| In your models | What CloudKit needs |
|---|---|
@Attribute(.unique) | Don't use it. CloudKit can't enforce uniqueness, because changes sync concurrently and at opportune times. |
| Relationships | Make every relationship optional. The iCloud servers don't guarantee atomic processing of relationship changes. |
| Inverse relationships | SwiftData sets the inverse for you only if it can infer it reliably from the schema. Otherwise, set it explicitly before saving, because CloudKit processes changes in an indeterminate order. |
Delete rule .deny | Not supported, because changes aren't synchronized immediately. Use another rule such as .cascade or .nullify. |
| Attributes | Give each a default value or make it optional (see below). |
The last row is not on Apple's SwiftData page. We saw it when Core Data loaded a store with a SwiftData model and CloudKit turned on: it failed with "CloudKit integration requires that all attributes be optional, or have a default value set." In our run, a default given only inside init() did not count, so write the default on the property itself.
@Model
final class Trip {
var name: String = ""
var startDate: Date = Date()
var notes: String?
@Relationship(deleteRule: .cascade, inverse: \Stay.trip)
var stays: [Stay]?
init(name: String) {
self.name = name
}
}
@Model
final class Stay {
var place: String = ""
var trip: Trip?
init(place: String) {
self.place = place
}
}
If you are moving an existing Core Data app, our Core Data to SwiftData converter produces models, and the checker above reads them against this list. Apple's Core Data article for CloudKit also asks that all relationships have an inverse and that entities in a configuration don't relate to entities in another one.
Create the development schema
During development, CloudKit has a draft schema in the development environment, which you can rewrite as often as you like. Apple's article shows how to have it created from a debug build: build a ModelConfiguration, point an NSPersistentStoreDescription at its url, set the container identifier, load the store synchronously, make a managed object model from your SwiftData classes, and call initializeCloudKitSchema() on an NSPersistentCloudKitContainer. Then remove the store before SwiftData's own container loads, so the two don't both sync.
import CoreData
#if DEBUG
func initializeSchema() throws {
let config = ModelConfiguration()
try autoreleasepool {
let desc = NSPersistentStoreDescription(url: config.url)
desc.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.com.example.Trips")
desc.shouldAddStoreAsynchronously = false
if let mom = NSManagedObjectModel.makeManagedObjectModel(for: [Trip.self, Stay.self]) {
let container = NSPersistentCloudKitContainer(name: "Trips", managedObjectModel: mom)
container.persistentStoreDescriptions = [desc]
container.loadPersistentStores { _, err in
if let err { fatalError(err.localizedDescription) }
}
try container.initializeCloudKitSchema()
if let store = container.persistentStoreCoordinator.persistentStores.first {
try container.persistentStoreCoordinator.remove(store)
}
}
}
}
#endif
Wrap it in #if DEBUG, as Apple does, so it never runs in a release build. Afterwards, open CloudKit Console and look for your record types. If they don't show up, Apple says you may need to turn on query support for them.
Deploy the schema to production
Apps in the App Store can access only the production environment, and Apple says you must deploy the development schema there before you publish. In CloudKit Console:
- Sign in at icloud.developer.apple.com and open the CloudKit Database app.
- Choose your container.
- Select Deploy Schema Changes.
- Review the pending changes and click Deploy.
Deploying copies record types, fields and indexes, but not records. You need edit rights to the production environment; an individual developer has them, and on a team the administrator does the step or grants the privilege.
After that, the schema is additive. You can add record types and fields, but you can't delete or change record types and fields that are already in production. Apple's strategies for later changes are to move people to a new store in a new container, to add fields step by step so older app versions still see every record (but not every field), or to keep a version attribute and fetch only records the current app understands. Plan the first production schema with this in mind; names you pick now stay.
Private database only
Look at the list of values above: the default, .private(_:) and .none. SwiftData's CloudKitDatabase offers no public or shared option. Apple's Core Data setup article, which describes the machinery SwiftData builds on, speaks of local stores "backed by a CloudKit private database". In practice that makes it sync between one person's own devices through their own iCloud account, and design your features on that basis. Apple's SwiftData pages say nothing about sharing records between different people, so we leave that out.
Sources
All read on 1 November 2026.
- Syncing model data across a person's devices, SwiftData
- ModelConfiguration and ModelConfiguration.CloudKitDatabase
- Deploying an iCloud Container's Schema, CloudKit
- Creating a Core Data Model for CloudKit
- Setting Up Core Data with CloudKit
Apple's requirements change; check the pages above for current details.