8

I'm using NSPersistentCloudKitContainer to synchronise data between different devices with CloudKit. It works perfectly well with a new project, however when I'm using it with old projects the old data which was added with NSPersistentContainer does not synchronise.

What I would like to achieve is to synchronise old data that was added with NSPersistentContainer after changing it to NSPersistentCloudKitContainer. Is it possible?

Wojtek
  • 1,006
  • 11
  • 30
  • As we are now at beta 5, and syncing of old data still doesn't happen, I am wondering if this is by design. Maybe there may be a way of syncing, by re-saving the original data, while controlled my the NSPersistentCloudKitContainer. Did you get any further yourself? – GL Apps Jul 29 '19 at 20:06
  • Indeed maybe it's by design, in release notes of the betas there is nothing about this issue. The only way that I found for now is changing something in orginal data and saving it. – Wojtek Jul 30 '19 at 08:11
  • I'm really hoping that we will be able to sync existing data as this would be the whole point of it for existing users of my App! Did you have to save every entity again for it to work? I also wish there was a way to see the progress of the iCloud sync as it seems awfully slow. – Paul Martin Aug 04 '19 at 04:06

1 Answers1

4

I've found a solution that works for my Core Data database - and mine is quite complex with multiple many-to-many relationships (A surgery/anaesthesia logbook app called Somnus)

I started by creating a new attribute for all my Core Data Entities called sentToCloud and setting it to FALSE by default in the Core Data model.

On the first load for an existing user:

  • Fetch request using the predicate "sentToCloud == FALSE" for each Entity type
  • Change sentToCloud to TRUE for each Object then save the MOC
  • This triggers NSPersistentCloudKitContainer to start syncing

I've done this in order of 'priority' that works for my database, assuming the iCloud sync sessions match the order in which Core Data is modified. In my testing this seems to be the case:

  • I first sync all child (or most child-like) Entities
  • Then sync their parents, and so on, up the tree
  • I sync the Object the user interacts with last, once everything else is in place so the relationships are intact and they don't think their data is borked while we wait for NSPersistentCloudKitContainer to reconnect all the relationships
  • I also leave any binary data (magically turned into a CKAsset behind-the-scenes) to last as it's not the most important part of my database

My database synced successfully from my iPad to iPhone and all relationships and binary data appear correct.

Now all I need is a way to tell the user when data is syncing (and/or some sort of progress) and for them to turn it off entirely.

ADDENDUM

So I tried this again, after resetting all data on the iCloud dashboard and deleting the apps on my iPhone & iPad.

Second time around it only synced some of the data. It seems like it still has a problem dealing with large sync requests (lots of .limitExceeded CKErrors in the console).

What's frustrating is that it's not clear if it's breaking up the requests to try again or not - I don't think it is. I've left it overnight and still no further syncing, only more .limitExceeded CKErrors.

Maybe this is why they don't want to sync existing data?

Personally, I think this is silly. Sometimes users will do a batch process on their data which would involve updating many thousands of Core Data objects in one action. If this is just going to get stuck with .limitExceeded CKErrors, NSPersistentCloudKitContainer isn't going to be a very good sync solution.

They need a better way of dealing with these errors (breaking up the requests into smaller requests), plus the ability to see what's going on (and perhaps present some UI to the user).

I really need this to work because as it stands, there is no way to synchronise many-to-many Core Data relationships using CloudKit.

I just hope that they're still working on this Class and improving it.

Paul Martin
  • 699
  • 4
  • 13
  • It seems you can turn off iCloud Syncing on a per-device basis (in Settings -> iCloud) but when you do, all data on the device is deleted... and it seems like it deletes all iCloud data too, which propagates to other devices (even if you *haven't* turned off iCloud on them) - that's a bit scary if you ask me... – Paul Martin Aug 08 '19 at 10:28
  • Update to above comment: If you turn off iCloud syncing on a device the server data seems to remain, and will repopulate your app if you turn it back on again. But there doesn't seem to be a way to disconnect iCloud syncing AND keep the local data. Would be nice to give users a choice. – Paul Martin Aug 24 '19 at 08:27
  • A way to keep local data but turn off syncing is to have a setting within your app which toggles your NSPersistentContainer to either be a vanilla NSPersistentContainer or an NSPersistentCloudKitContainer. I've found if doing this you must set an NSPersistentHistoryTrackingKey on the vanilla container. – Paul Martin Oct 21 '19 at 20:30