💡 Fix iCloud Sync Issues with initializeCloudKitSchema

Get weekly handpicked updates on Swift and SwiftUI!

TL;DR: Use initializeCloudKitSchema to resolve iCloud sync issues in Core Data or SwiftData. Call it after enabling sync or updating the data model, then comment it out for future stability.

Background

In iOS development, when implementing iCloud data synchronization using Core Data or SwiftData, developers often encounter incomplete data sync issues. While some operations sync correctly, newly created data may fail to appear in the cloud. This issue is frequently related to the initialization of the CloudKit schema.

Why Use initializeCloudKitSchema?

If you notice discrepancies between your local data model and the CloudKit schema in the CloudKit Dashboard, it’s likely because the initializeCloudKitSchema method has not been properly utilized. While CloudKit may automatically generate a schema when the first data is created in simple models, automatic creation often fails in the following scenarios:

  • The data model contains complex relationships
  • The initial data creation does not involve all relationship objects
  • The model structure has been modified

When to Use initializeCloudKitSchema

You should use initializeCloudKitSchema in the following situations:

  1. When enabling iCloud sync for the first time
  2. After making modifications to your local data model

Implementation in Core Data

For Core Data, initializeCloudKitSchema needs to be called after the NSPersistentCloudKitContainer has successfully loaded:

Swift
let container = NSPersistentCloudKitContainer(name: "Model", managedObjectModel: mom)
container.persistentStoreDescriptions = [desc]
container.loadPersistentStores { _, error in
    if let error {
        fatalError(error.localizedDescription)
    }
}

// Initialize CloudKit Schema after loading persistent stores
try container.initializeCloudKitSchema() // Can be commented out after execution

Implementation in SwiftData

For SwiftData, you need to convert your SwiftData model into Core Data’s NSManagedObjectModel before initializing the CloudKit schema:

Swift
let config = ModelConfiguration()

do {
#if DEBUG
    try autoreleasepool {
        let desc = NSPersistentStoreDescription(url: config.url)
        let options = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.com.example.Trips")
        desc.cloudKitContainerOptions = options
        desc.shouldAddStoreAsynchronously = false
        
        if let mom = NSManagedObjectModel.makeManagedObjectModel(for: [Trip.self, Accommodation.self]) {
            let container = NSPersistentCloudKitContainer(name: "Trips", managedObjectModel: mom)
            container.persistentStoreDescriptions = [desc]
            container.loadPersistentStores { _, error in
                if let error {
                    fatalError(error.localizedDescription)
                }
            }
            
            // Initialize CloudKit Schema
            try container.initializeCloudKitSchema()
            
            // Remove store after initialization
            if let store = container.persistentStoreCoordinator.persistentStores.first {
                try container.persistentStoreCoordinator.remove(store)
            }
        }
    }
#endif
    modelContainer = try ModelContainer(for: Trip.self, Accommodation.self,
                                      configurations: config)
} catch {
    fatalError(error.localizedDescription)
}

Important Notes

  1. One-Time Execution: You only need to execute initializeCloudKitSchema once after each model update.
  2. Post-Execution Cleanup: After successfully initializing the schema, you can comment out the initialization code to avoid redundant calls.

Additional Resources

By properly utilizing initializeCloudKitSchema, you can ensure that your CloudKit schema matches your local data model, resolving issues with incomplete iCloud data synchronization.