0

I want allow toggling iCloud sync of a CoreData database on and off. When the user toggles the setting, updateContainer() is called. Sync works if the app launches with iCould sync enabled. But after iCould was toggled off and back on, sync is not working.

In the spammy CloudKit logs in the console I found two suspicious warnings: CloudKit setup failed because there is another instance of this persistent store actively syncing with CloudKit in this process. and Told to tear down with reason: Error NSCocoaErrorDomain:134410

Has someone experience on how to achieve this and has an idea what's wrong here?

Here's my PersistenceController.swift

import CoreData

class PersistenceController {
    static var shared = PersistenceController()
    
    lazy var container: NSPersistentContainer = {
        setupPersistentContainer()
    }()
    
    func updateContainer() {
        saveContext()
        container = setupPersistentContainer()
    }
    
    func saveContext() {...}
    
    private func setupPersistentContainer() -> NSPersistentContainer {
        // this value is synchronised across devices via NSUbiquitousKeyValueStore in iCloud
        let iCloudSyncEnabled = UserDefaults.standard.bool(forKey: iCouldSyncKey)
        
        var persistentContainer = getPersistentContainer(iCloudSync: iCloudSyncEnabled)
        
        guard let description = persistentContainer.persistentStoreDescriptions.first else {
            fatalError("Could not get persistentStoreDescription")
        }
        
        // Set merge policy
        persistentContainer.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        
        // Enable HistoryTracking: This allows a non-iCloud persistent container to keep track of changes if a user changes their mind and turns it on.
        description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        
        if iCloudSyncEnabled {
            persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
            description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
        }
        
        persistentContainer.loadPersistentStores { storeDescription, error in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error)")
            }
        }
        
        return persistentContainer
    }
    
    private func getPersistentContainer(iCloudSync iCloudSyncEnabled: Bool) -> NSPersistentContainer {
        let modelName = "Model"
        if iCloudSyncEnabled {
            print("Using NSPersistentCloudKitContainer")
            return NSPersistentCloudKitContainer(name: modelName)
        } else {
            print("Using NSPersistentContainer")
            return NSPersistentContainer(name: modelName)
            
        }
     }
}

This approach is partially based on CoreData+CloudKit | On/off iCloud sync toggle

hri
  • 67
  • 7

1 Answers1

0

I ended up not implementing a toggle switch at all, because I learned that offering the option to toggle of iCould Sync in the app is generally not recommended by apple. Instead apps should rely on the switch in the system settings in which the user can toggle iCould capabilities for each app. So I'm now showing information whether sync is active or disabled in my app and link to the settings if users want to change their sync setting.

hri
  • 67
  • 7