I see folks implement an in-app toggle to enable / disable CloudKit synchronization when using NSPersistentCloudKitContainer
. I honestly don't see that a great idea. I know this is controversial, and so would layout my reasoning here:
First, the system already provides a setting (Settings > Apple ID > iCloud) that allows users to turn on / off iCloud for an app. If a user turns off iCloud for an app with the setting, the app won’t be able to synchronize with CloudKit, even the in-app toggle is on.
Secondly, for the toggle to work, the app needs to release the Core Data objects currently in use, and reload the Core Data stack. In your case, you achieve that by asking the user to restart the app, which is probably not a great experience.
Last but probably more importantly, CloudKit implements access control on its data: only the store owner (the user who owns the login iCloud account when the store is created) can access a store associated with a CloudKit private or shared database, and only the data owner (the user who owns the login iCloud account when the data is created) can modify the data in a store associated with a CloudKit public database by default.
When a Core Data store is tied to a CloudKit database, NSPersistentCloudKitContainer
enforces the access control. For example, it wipes off the Core Data store on the device when it gets notified that the user logs out iCloud. (This doesn’t lead to data loss, because NSPersistentCloudKitContainer
downloads the data from CloudKit when the user logs back in with the same iCloud account.) Using NSPersistentContainer
, or NSPersistentCloudKitContainer
with cloudKitContainerOptions
being set to nil, to manage an existing Core Data + CloudKit store loses the access control, and may introduce data privacy and security concerns.
To be more concrete, consider the following flow:
a. A user uses the in-app toggle to turn off CloudKit synchronization. As a result, the app sets cloudKitContainerOptions
to nil and reload the store, and the store isn't tied to a CloudKit database anymore.
b. The current user logs out the device.
c. A new user launches the app and add more data.
d. The original user logs in again and toggles CloudKit synchronization back on, which reloads the store with cloudKitContainerOptions
being set to a valid value.
At step c, NSPersistentCloudKitContainer
doesn't enforce the access control, and so the existing data spreads to the new user.
At step d, it is unclear to the system who owns the data added at step c and how to handle it. I believe the behavior today is that NSPersistentCloudKitContainer
resumes the synchronization, assuming that the data belongs to the orignal user, which may or may not be right.
Having said that, given you have implemented the in-app toggle, I'd like to comment a bit on your immediate question:
"Some users of my app are reporting total loss of data while using the app. This is happening specifically when they enable iCloud sync."
...
"Apart from that I periodically run the clear history"
This may be the culprit. NSPersistentCloudKitContainer
relies on the history to figure out the changes on the store since last export. If the history is purged before being processed by NSPersistentCloudKitContainer
, the changes happened the local device won't be synchronized to CloudKit.
It is typically no harm to leave the history there, but if you do need to purge it because the size of your store grows to very large, consider following the suggestion mentioned in the last paragraph in the following article:
Best,
——
Ziqiao Chen
Worldwide Developer Relations.