Search code examples
swiftcore-datanspersistentcloudkitcontainer

NSPersistentCloudKitContainer and persistent history tracking


I'm building an application that makes use of NSPersistentCloudKitContainer. The app doesn't have sharing functionality and its only backend functionality is to use the cloudkit container to sync data across a user's devices. The setup is fairly barebones, instantiating a container, setting up a single store description, and loading the stores.

My big question: Do I need to do anything with persistent history tracking? I have yet to find a concrete answer to this question but from what I can tell, persistent history tracking is used for merging in changes that happen in one target, such as an extension, into another. It doesn't sound like I need it to take full advantage of iCloud sync.


Solution

  • You DO need to enable persistent history tracking so that the device is able to catch up if a user enables/disables/re-enables your Apps use of iCloud by turning off your Apps access to iCloud Drive or via some feature you've implemented.

    NSPersistentCloudKitContainer uses the history and handles it for you behind the scenes. You do NOT need to do anything with history, although you can if you want. The Apple Docs are a little fuzzy on that point.

    What I did (and deployed) was to give users a switch in the App to allow them to enable/disable the Apps use of iCloud. It is specific to the App on that device and the setting is NOT persisted via ubiquitous defaults to any other device. I prefer not to encourage them to disable their main source of backup. All it does to disable is set the Container Identifier to nil and when enabled to the container ID string as shown below. This was stated as OK to do by an Apple CloudKit engineer in the Apple Developer Forums. When a user flips this I instruct them to fully restart the App. So far it seems harmless and in all my testing it works fine.

    Set your CoreData stack like this:

    @objc func initCoreDataStack()
    {
        guard let description = pc.persistentStoreDescriptions.first else {
            fatalError("*** CoreDataUtil - could not find persistent store description.")
        }
        description.setOption(true as NSNumber, forKey:NSPersistentHistoryTrackingKey)
        description.setOption(true as NSNumber, forKey:NSPersistentStoreRemoteChangeNotificationPostOptionKey)
    
        //
        //  If user wants iCloud Storage set id, else set it to nil
        //  but with history on (see above) so that if they enable it
        //  later there is a history of changes.
        //
        if(useiCloud == true) {
            let cloudKitContainerIdentifier = "iCloud.your.container"
            let options = NSPersistentCloudKitContainerOptions(containerIdentifier:cloudKitContainerIdentifier)
            description.cloudKitContainerOptions = options
        }
        else {
            description.cloudKitContainerOptions = nil
        }
    
        pc.persistentStoreDescriptions.first?.url = storeURL()
        pc.viewContext.automaticallyMergesChangesFromParent = true
        pc.loadPersistentStores { _, error in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
            // Set this here so that updates by consumers are dynamic
            self.pc.viewContext.automaticallyMergesChangesFromParent = true
            // My code does this for consumers of this singleton.
            self.sendMOCReadyNotificationForPlatform()
        }
    }