Synchronizing Core Data to CloudKit Public Database

Viewed 1701
2 Answers

This is now possible in iOS 14.0+ Beta and macOS 11.0+ Beta via the new databaseScope property: https://developer.apple.com/documentation/coredata/nspersistentcloudkitcontaineroptions/3580372-databasescope

The possible values are .public (the public database), .private (the private database) and .shared (the shared database).

E.g.:

let container = NSPersistentCloudKitContainer(name: "test")
guard let description = container.persistentStoreDescriptions.first else {
    fatalError("Error")
}
description.cloudKitContainerOptions?.databaseScope = .public

The video https://developer.apple.com/videos/play/wwdc2020/10650 describes how to sync the Core Data store with the CloudKit public database by setting the databaseScope value to .public.

You may also need an #available check to ensure backwards compatibility, ie:

if #available(iOS 14.0, *) {
    description.cloudKitContainerOptions?.databaseScope = .public
} else {
    // Fallback on earlier versions
}

To answer last question in comments, yes it is possible use .public and . private on the same store. This is the link to the project: Messanger by Patrick Maltagliati on 10/2/20

   init(inMemory: Bool = false) {
    container = NSPersistentCloudKitContainer(name: "Messanger")
    
    let defaultDesctiption = container.persistentStoreDescriptions.first
    let url = defaultDesctiption?.url?.deletingLastPathComponent()
    
    let privateDescription = NSPersistentStoreDescription(url: url!.appendingPathComponent("private.sqlite"))
    let privateOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.com.Maltagliati.Messanger")
    privateOptions.databaseScope = .private
    privateDescription.cloudKitContainerOptions = privateOptions
    privateDescription.configuration = "Private"
    privateDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
    privateDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
    
    let publicDescription = NSPersistentStoreDescription(url: url!.appendingPathComponent("public.sqlite"))
    let publicOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.com.Maltagliati.Messanger")
    publicOptions.databaseScope = .public
    publicDescription.cloudKitContainerOptions = publicOptions
    publicDescription.configuration = "Public"
    publicDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
    publicDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
    
    container.persistentStoreDescriptions = [privateDescription, publicDescription]
    
    if inMemory {
        container.persistentStoreDescriptions.forEach { $0.url = URL(fileURLWithPath: "/dev/null") }
    }
    
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
}
Related