NSPersistentCloudKitContainer stop sync when CKErrorDomain 12 error occurred

My app uses NSPersistentCloudKitContainer to implement Coredata and iCloud synchronization data

Recently, I received feedback from online users about data synchronization errors, and the error code is CKErrorDomain 12 .

In the App, I use NSPersistentCloudKitContainer.eventChangedNotification to monitor the synchronization status, the following is the specific code

NotificationCenter.default.publisher(for: NSPersistentCloudKitContainer.eventChangedNotification)
            .sink(receiveValue: { notification in
                if let cloudEvent = notification.userInfo?[NSPersistentCloudKitContainer.eventNotificationUserInfoKey]
                    as? NSPersistentCloudKitContainer.Event {
                    let event = SyncEvent(from: cloudEvent) 
                }
            })
            .store(in: &disposables)

When the user feedbacks that the data cannot be synchronized, it can be seen from the above code that an error occurred when CoreData + iCloud was exporting data.

At the same time, cloudKitEvent.error does not contain any error information, only CKErrorDomain 12 this information, I don’t know how to troubleshoot the error at all.

What's even more frightening is that when the CKErrorDomain 12 error occurs, the app's synchronization service will stop immediately. Trying to restart the app or restart the phone, and turn off iCloud synchronization in the system, will not make the synchronization service work again.

Only uninstalling and reinstalling can completely solve this problem, but users will also lose some data after uninstalling, because when the error occurs, any data generated by the user has not been successfully synchronized to iCloud, so these data will be lost after uninstalling.

The following is the code for CoreDataStack initialization

private func setupContainer(allowCloudKitSync: Bool) -> NSPersistentCloudKitContainer {
    let container = NSPersistentCloudKitContainer(name: containerName)
    let privateStoreURL = containerURL.appendingPathComponent(privateStoreName)
    let privateDescription = NSPersistentStoreDescription(url: privateStoreURL)
    let privateOpt = NSPersistentCloudKitContainerOptions(containerIdentifier: identifier)
    
    privateDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
    privateDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
    
    "allowCloudKitSync = \(allowCloudKitSync)".debugLog("CoreDataStack")
    
    if allowCloudKitSync {
        privateDescription.cloudKitContainerOptions = privateOpt
    } else {
        privateDescription.cloudKitContainerOptions = nil
    }

    container.persistentStoreDescriptions = [privateDescription]
    
    container.loadPersistentStores(completionHandler: { [weak self] (storeDescription, error) in
        if let error = error as NSError? {
            self?.dbStatePublish.send(.loadError)
            #if DEBUG
            "Unresolved error \(error), \(error.userInfo)".debugLog("CoreDataStack")
            #endif
        } else {
            if storeDescription.cloudKitContainerOptions == nil {
                self?.coreDataState = .local
            } else {
                self?.coreDataState = .cloud
            }

            "load coredata status = \(String(describing: self?.coreDataState))".debugLog("CoreDataStack")
        }
    })
    
    let options = NSPersistentCloudKitContainerSchemaInitializationOptions()        
    try? container.initializeCloudKitSchema(options: options)

    container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
    container.viewContext.transactionAuthor = appTransactionAuthorName

    container.viewContext.automaticallyMergesChangesFromParent = true
    do {
        try container.viewContext.setQueryGenerationFrom(.current)
    } catch {
        fatalError("###\(#function): Failed to pin viewContext to the current generation:\(error)")
    }
    
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(storeRemoteChange(_:)),
                                           name: .NSPersistentStoreRemoteChange,
                                           object: container.persistentStoreCoordinator)
    
    return container
}
NSPersistentCloudKitContainer stop sync when CKErrorDomain 12 error occurred
 
 
Q