NSPersistentCloudKitContainer not syncing existing data

I'm running into trouble setting up sync using NSPersistentCloudKitContainer on an existing app. The app doesn't seem to sync data that was added to Core Data prior the app's iOS 13 update. For instance:


1. I install an existing, pre-iOS 13, version of the app and create several records that are stored in Core Data.

2. Over that existing install, I install the updated version that uses NSPersistentCloudKitContainer and create some additional new records.

3. I install the same new version on a second device.


Results: Records created in step 1 don't appear on the second device, but records created in step 2 do.


Is there a step I'm missing in order to get existing data to sync? I'm wondering if this has something to do with the persistent history token, but I'm unsure how to tell NSPersistentCloudKitContainer it should "start over" on first launch.

  • You will need to migrate your existing core data store with NSPersistentHistoryTrackingKey enabled. This will then allow your old data to be sync'd using NSPersistentCloudKitContainer.

  • I had the same issue and so I enabled both options description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)

    the issue still reproducible

Add a Comment

Replies

Update: Filed a feedback report since this seems to be an issue others are encountering as well. (See https://www.andrewcbancroft.com/blog/ios-development/data-persistence/nspersistentcloudkitcontainer-buggy-behavior-list/)

Just utilizing the container doesn't enable the sync. You have to add/request the capabilities throuigh your plist file (iCloud under capabilities, may require background too, but i'm unsure). Pretty sure you need remote notifications as well. I read on a blog somewhere a step by step to get it working, but I haven't found 'Official' instructions for anything more than the class declaration yet. I 'think' I googled NSPersistentCloudKitContainer and tutorial.. or maybe getting started.

Let's hope this is addressed because this is a huge issue.

You missed his point. Data entereed after setting up NSPersistentCloudKitContainer synced. It was pre-existing data which did not sync. For a person with a core data app who wants to use CloudKit, this problem is huge. And I'll bet that the original author signed up for remote notifications. It's not that he didn't get a notification, he didn't get the data.

I've got the same issue. 1000s of records in the existing Core Data database. All new data syncs perfectly, but the old data stays on just one device.


Do we need to do something to trigger the sync? Mark records for sync? Maybe iterate the records, and re-save each one?


We're at beta 5 now, and it still doesn't work. I'm beginning to think it's by design, not a bug.

I also filed a feedback request. I hope that everybody that needs to have pre-existing data synced, do so as well.

I received feedback from Apple on this. I hope youi did as well.

Do these records have entries in persistent history? Without them NSPersistentCloudKitContainer can't "see" the records.


This is by design. However we will take your feedback reports on this issue as an enhancement request to make it easier to use NSPersistentCloudKitContainer with existing store files.


Keep filing them. Include a sysdiagnose and the persistent store files if you can.

I'm thinking about creating a UUID attribute and adding that to every entity in my core data model. Then, I will fetch every entity and add the UUID to it. And then save. Hopefully, that will trigger a sync of the pre-existing data.

Hi Nick, my existing CoreData container has no persitent history enabled. So existing records are not seen by NSPersitentCloudKitContainer. What would be a way to enable history tracking on those records to get them pushed to iCloud?

I can think of a 'hack' by adding a boolean flag to all entities and updating them once on the first launch with NSPersitentCloudKitContainer enabled, but that feels wrong.

DId you get a reply to your feedback report? And did you find a workaround for this issue?

Adding a UUID attribute for each entity in my model and setting a uuid worked for me. My pre-existing data synced as a result.

  • Do you mean did you set UUID value again so that existing entities gets updated or did you add a new property and set UUID?

Add a Comment

Nick, thanks for the input.


Behaviours I've noticed...


Scenario 1

Existing Core Data app with existing records.

Log in to the same Apple ID on different Simulators or Devices.

Enable NSPersistentCloudKitContainer and include container.viewContext.automaticallyMergesChangesFromParent = true

Only new records are synced to users CloudKit account and therefore across devices using the same Apple ID. Existing records remain only on the device on which they are created.


Scenario 2

Existing Core Data app with existing records.

Log in to the same Apple ID on different Simulators or Devices.

Enable NSPersistentCloudKitContainer and include container.viewContext.automaticallyMergesChangesFromParent = true

Enable NSPersistentHistoryTrackingKey...

guard let containerStoreDescription = container.persistentStoreDescriptions.first else {

fatalError("\(#function): Failed to retrieve a persistent store description.")

}

containerStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)

ANY records entered with NSPersistentHistoryTrackingKey = true are synced to users CloudKit account and therefore across devices that are using the same Apple ID.

Records entered prior to setting NSPersistentHistoryTrackingKey = true are never synced to users CloudKit account and remain only on the device on which they are created..

If NSPersistentCloudKitContainer is switched to NSPersistentContainer, run a few times, then switched back to NSPersistentCloudKitContainer, even the previous records entered with NSPersistentHistoryTrackingKey = true are synced to users CloudKit account... in fact, so long as the CloudKit development environment is not reset, the app can be deleted from all devices and again, previous records entered with NSPersistentHistoryTrackingKey = true are synced to users CloudKit account and therefore across devices. To reiterate, any records entered prior to setting NSPersistentHistoryTrackingKey = true are never synced to users CloudKit account and remain only on the device on which they are created.

Note that I have found it is necessary to set NSPersistentHistoryTrackingKey = true when switching from NSPersistentCloudKitContainer back to NSPersistentContainer (only done for testing purposes during my efforts to sync existing records).


Scenario 3

Existing Core Data app with no existing records.

Log in to the same Apple ID on different Simulators or Devices.

Enable NSPersistentCloudKitContainer and include container.viewContext.automaticallyMergesChangesFromParent = true

ALL records are synced to users CloudKit account and therefore across devices that are using the same Apple ID.

Unnecessary to separately enable NSPersistentHistoryTrackingKey.


Some developers have suggested some interesting (and functional) workarounds. The most interesting to me is to set a bool for each existing record following the successful load of an NSPersistentCloudKitContainer. Toggling this bool for each record after loading the NSPersistentCloudKitContainer forces a sync for those existing records. While it is the best workaround I have stumbled upon so far, this also seems extremely cumbersome to me and I cannot bring myself to write the code to make this work.


Surely there must be a mechanism to apply NSPersistentHistoryTrackingKey = true to all previous records? That is what I will be focussing on next, so any hints or guidance would be gratefully appreciated.

Hi Andrew, did you have any luck finding another mechanism for enabling syncronization for existing records that were created before NSPersistentHistoryTrackingKey was set to true? I'm starting to implement the bool hack now but it indeed is really cumbersome.

Still working on it roel,

Although it is a clever workaround, I still cannot accept the bool hack as a reasonable solution.

I've logged a feedback report with Apple, no response yet.

Currently preparing a Developer TSI on the matter.

If I find a solution I'll add a response to this thread.