NSPersistentCloudKitContainer and persistent history tracking
Asked Answered
R

1

6

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.

Resht answered 16/3, 2022 at 5:58 Comment(4)
I believe persistent history is used to sync with all external changes, like an app extension, or a remote database (CloudKit)Meadowlark
You'll find that more recently (and I'm sorry I cannot quote on when this came into effect) but NSPersistentHistoryTracking is automatically implemented for each NSPersistentCloudKitContainer. What you might need to be aware of is that if your store (for example the sqlite file) existed before you implemented NSPersistentCloudKitContainer (i.e. its was simply NSPersistentContainer) then history tracking would not have been automatically implemented and therefore only newly inserted objects will register with CloudKit.Crackling
That actually sounds right, I just can't find anything that confirms that's the caseResht
I couldn’t find a source either, but when creating a fresh project with both Core Data + CloudKit enabled and printing out the store description options without making any changes, I could see NSPersistentHistoryTrackingKey: 1 so it appears to be enabled by default.Villatoro
E
9

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()
    }
}
Externalize answered 14/6, 2022 at 11:36 Comment(6)
"You do NOT need to do anything with history, although you can if you want." Will NSPersistentCloudKitContainer remove the history after it has been consumed?Billiebilling
Don't know. That is an Apple subsystem that I assume they maintain and clean up as they see fit. I personally would not dare touch it.Externalize
Was reading the Core data docs and it turns out it is developers responsibility to delete the history and it is highly recommended as it saves the disk space. developer.apple.com/documentation/coredata/…Billiebilling
Wow, that's new. Good info. Thanks!Externalize
@Billiebilling reading a bit more, I am going to point out my discussion was for users using history for CloudKit. CloudKit uses it behind the scenes and will enable history on a NEW container (not old) silently in the background. Devs have to enable it on older containers being converted to use CloudKit. If cloudkit enables and uses silently I assume it cleans history up. The discussion you ref above is for CoreData. If you are using history in that context and not as part of CloudKit then perhaps you need to cleanup. My App uses NSPersistentCloudKitContainer regardless of if user wants Cloudkit.Externalize
NSPersistentCloudKitContainer and NSPersistentContainer are almost same in terms of behaviour as former is a subclass of latter. So, my assumption is, history management should not differ. Will it be possible for you to check if your app is purging the old history using NSPersistentHistoryChangeRequest.fetchHistory(after: Date(timeIntervalSince1970: 0)). Check if your first transaction remains the same over timeBilliebilling

© 2022 - 2024 — McMap. All rights reserved.