A question on account change handler code in CKSyncEngine demo project

I have a quesiton on .accountChange handler code in CKSyncEngine demo project. Below is the code in handleAccountChange():

    if shouldDeleteLocalData {
        try? self.deleteLocalData() // This error should be handled, but we'll skip that for brevity in this sample app.
    }

    if shouldReUploadLocalData {
        let recordZoneChanges: [CKSyncEngine.PendingRecordZoneChange] = self.appData.contacts.values.map { .saveRecord($0.recordID) }
        self.syncEngine.state.add(pendingDatabaseChanges: [ .saveZone(CKRecordZone(zoneName: Contact.zoneName)) ])
        self.syncEngine.state.add(pendingRecordZoneChanges: recordZoneChanges)
    }

IMHO, when user switches account, the most important thing is to reload data from the new account's document folder. However, I can't see this is done anywhere. In above code, if shouldDeleteLocalData is false, self.appData would still hold the previous account's local data. That seems very wrong. Am I missing something?

It would be best if iOS restarts all applications when user switches account. If that's not the case (I guess so, otherwise there is no point to handle .accountChange in the app), I think application should implement an API to re-initialize itself.


EDIT: after looking at the code again, I realize that the following code makes sure shouldDeleteLocalData is always true when user switching accounts. So the code doesn't leak the previous account's data, though I still think it has an issue - it doesn't load the new account's data.

        case .switchAccounts:
            shouldDeleteLocalData = true
            shouldReUploadLocalData = false
Answered by Apple Staff in 811949022

deleteLocalData clears out the in-memory AppData but also persists the cleared AppData on disk (by calling try self.persistLocalData()) — essentially starting fresh, with no local contacts in memory or persisted on disk.

The final line in deleteLocalData, self.initializeSyncEngine(), sets up a new CKSyncEngine, importantly with stateSerialization: self.appData.stateSerialization which is now fresh. As long as automaticallySync is true, CKSyncEngine schedules an initial sync on init, and since we're starting fresh with this instance of CKSyncEngine, it will go and fetch everything from the server (i.e. all contacts saved under the new account) and handle them locally.

Hi rayx,

In the above case (.switchAccounts), as shouldDeleteLocalData is true, at the bottom of the function deleteLocalData() is called. That clears syncengine's current state (stateSerialization) and re-initializes, which will cause the engine to fetch the new account's data from CloudKit from scratch.

In the above case (.switchAccounts), as shouldDeleteLocalData is true, at the bottom of the function deleteLocalData() is called. That clears syncengine's current state (stateSerialization) and re-initializes, which will cause the engine to fetch the new account's data from CloudKit from scratch.

Thanks. May I ask how re-initialization could " cause the engine to fetch the new account's data from CloudKit from scratch"?

In my understanding deleteLocalData() resets appData and calls initializeSyncEngine() to create a new CKSyncEngine instance. There is no code to load local data from the new account's document folder into appData. That part of code is in SyncedDatabase actor's init, which I don't think get re-called in this scenario. So I suspect, after switching new account, the app will show an empty list even if the new account has data on iCloud. Am I missing something?

PS: I can't do the experiment at the moment because I don't have a proper environment to compile the project (I'm wating for the new mac mini 7 :)

Accepted Answer

deleteLocalData clears out the in-memory AppData but also persists the cleared AppData on disk (by calling try self.persistLocalData()) — essentially starting fresh, with no local contacts in memory or persisted on disk.

The final line in deleteLocalData, self.initializeSyncEngine(), sets up a new CKSyncEngine, importantly with stateSerialization: self.appData.stateSerialization which is now fresh. As long as automaticallySync is true, CKSyncEngine schedules an initial sync on init, and since we're starting fresh with this instance of CKSyncEngine, it will go and fetch everything from the server (i.e. all contacts saved under the new account) and handle them locally.

As long as automaticallySync is true, CKSyncEngine schedules an initial sync on init, and since we're starting fresh with this instance of CKSyncEngine, it will go and fetch everything from the server (i.e. all contacts saved under the new account) and handle them locally.

Thanks a lot for the detailed explanation! I would never be able to figure it out myself if you didn't mention it.

A question on account change handler code in CKSyncEngine demo project
 
 
Q