Search code examples
iosswiftcore-datacloudkitnspersistentcloudkitcontainer

Core Data sharing with CloudKit: Unable to Accept Share


I had my app working with Core Data, then CloudKit to sync between devices and now I'd like to share data between users. I watched both Build apps that share data through CloudKit and Core Data and What's new in CloudKit WWDC21 and thought that I got the concepts down. CloudKit uses zone sharing and CKShares to handle sharing and Core Data attaches to this implementation natively in iOS15.

I setup my Core Data stack as such:

/// Configure private store
guard let privateStoreDescription: NSPersistentStoreDescription = persistentContainer.persistentStoreDescriptions.first else {
    Logger.model.error("Unable to get private Core Data persistent store description")
    return
}
privateStoreDescription.url = inMemory ? URL(fileURLWithPath: "/dev/null") : privateStoreDescription.url?.appendingPathComponent("\(containerIdentifier).private.sqlite")
privateStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
privateStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
persistentContainer.persistentStoreDescriptions.append(privateStoreDescription)

/// Create shared store
let sharedStoreDescription: NSPersistentStoreDescription = privateStoreDescription.copy() as! NSPersistentStoreDescription
sharedStoreDescription.url = sharedStoreDescription.url?.appendingPathComponent("\(containerIdentifier).shared.sqlite")
let sharedStoreOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: containerIdentifier)
sharedStoreOptions.databaseScope = .shared
sharedStoreDescription.cloudKitContainerOptions = sharedStoreOptions
persistentContainer.persistentStoreDescriptions.append(sharedStoreDescription)
persistentContainer.loadPersistentStores(...)

Implemented the SceneDelegate user acceptance:

func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
    let container = PersistenceController.shared.persistentContainer
    let sharedStore = container.persistentStoreCoordinator.persistentStores.first!
    container.acceptShareInvitations(from: [cloudKitShareMetadata], into: sharedStore, completion: nil) //TODO: Log completion
}

However after sharing the NSObject as such in my UI using UICloudSharingController as seen below:

let object: NSObject = // Get Object from view context
let container = PersistenceController.shared.persistentContainer
let cloudSharingController = UICloudSharingController { (controller, completion: @escaping (CKShare?, CKContainer?, Error?) -> Void) in
    container.share([object], to: nil) { objectIDs, share, container, error in
        completion(share, container, error)
        Logger.viewModel.debug("Shared \(household.getName())")
    }
}
cloudSharingController.delegate = self
self.present(cloudSharingController, animated: true) {}

My SceneDelegate method is never called and I get the following alert when I press the invite from the messages app. I'm not quite sure what is wrong in this case as on the CloudKit developer console I see the object in a private database with the zone of com.apple.coredata.cloudkit.share.[UUID]. I have not released the app yet so I'm not sure where it is getting version information from as both apps were launched from the Xcode debugger(same version & build). Additionally I was unable to find reference this alert on other questions so any advice, suggestions, or help is welcome as I have been stuck on this for a few evenings. Please let me know if there is more information that could shine light on this problem.


Solution

  • I had the same problem and it was solved when I added the CKSharingSupported key with a Bool value of true in the Info.plist

    After that I was able to share with no problem.