How to fix NSCloudKitMirroringDelegate unhandled exception after faulty model change

I have a complex data model in development mode, with a large amount of data, using CoreData with CloudKit sync. All worked fine, syncing from a Mac to an iPad Pro, until I made some unwise changes to the model and a couple of relationships. I should have known, but was hurrying and not thinking clearly. The App is not on the App Store: it's for my own use.

Records are now not synced to CloudKit: _error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _importFinishedWithResult:importer:]- (1371): <PFCloudKitImporter: 0x6000005d8080>: Import failed with error: Error Domain=NSCocoaErrorDomain Code=134421 "Import failed because applying the accumulated changes hit an unhandled exception." UserInfo={NSLocalizedFailureReason=Import failed because applying the accumulated changes hit an unhandled exception., NSUnderlyingException=* -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]}_**

It seems that there's a queue of faulty (non-matching) updates, which I can find no way of purging. Perhaps I could disconnect the CloudKit syncing in the app, use the CloudKit console to reset the development environment, then reconnect CloudKit syncing in the app - but would that invoke a repopulation of the CloudKit data, or cause a deletion of all CoreData data?

I can, with a few days' work, reimport all of the data - but that would, I assume, need a newly named app so as to create a new CloudKit container.

Any thoughts/solutions would be appreciated.

Regards, Michaela

Have you filed a feedback report with your store files and a sysdiagnose?

I've created a test app, with CoreData records synced via CloudKit. Records created on the Mac get synced to the iPad Pro. It's a very simple app, just Listing records from a FetchedResults.

I then Reset development environment in the CloudKit console Web Portal, with both devices' app closed. This Reset, of course, deleted all records, schema and coredata.cloudkit zone from the Development environment.

On restarting each app, CoreData records on both devices remained and the app worked correctly, fetching existing records and also recreated the CloudKit zone, schema and records. Thereafter new records on the Mac were correctly synced to the iPad Pro.

I'll do some more testing, making the test app more complex with the sort of relationship I was trying to achieve in the messed-up app, then might be bold enough to use this approach to fix the problem app.

However, it would still be better if I could somehow purge the CloudKit update queue - although I can't be sure that the schema is correct anyway: probably not.

Regards, Michaela

One root cause that we have identified for this issue is removing a many-to-many relationship from your model.

This is supposed to be a supported migration, so we are collecting feedback reports to track against that as a bug. If you would like to be notified of a fix for that share your feedback number here and we can relate them together.

As a workaround you can simply add the relationship back. Or, attempt a more detailed / invasive solution described below.

Our schema is public and described here:

https://developer.apple.com/documentation/coredata/mirroring_a_core_data_store_with_cloudkit/reading_cloudkit_records_for_core_data

  • Your container will contain CDMR records that identify the offending relationship.
    • The fields CD_entityNames and CD_relationships can be used to tell which relationship a record represents.
    • One (or more, depending on how many relationships you removed) of those records may be causing this issue.

You can choose to:

  1. Add the missing relationship back to your model
  2. Delete the offending records using a normal CKModifyRecords operation. On customer devices, you could query CloudKit using a CKQueryOperation for CDMR records that match the offending relationship and delete them. NSPersistentCloudKitContainer will consume those deletes on other devices as if they were changes it wrote.

One note of caution, #2 requires robust testing, as getting that wrong will prevent devices that still have the old model from making legitimate changes.

Fixed the problem by these steps:

  1. Created a backup of the MacMini's persistent store, using code for a temporary persistent store coordinator.migratePersistentStore.
  2. Also created CSV file backups of all objects of all entities (as a fall-back if the migrate didn't work correctly).
  3. Deleted all objects of all Entities in the MacMini persistent store.
  4. Waited for deletions (Step 3) to be propagated to the CloudKit container.
  5. Checked the CloudKit container (Web Console) and manually deleted any Entity Objects not deleted by Steps 3 and 4 - there were quite a few.
  6. Restored the MacMini persistent store by code for:

a persistentStoreCoordinator.destroyPersistentStore of the original MacMini store

b persistentStoreCoordinator.addPersistentStore based on the backup store

c persistentStoreCoordinator.migratePersistentStore from the backup to the "new" store (step 6 b)

  1. Waited for the "new store" Entity Objects to be propagated to the CloudKit container.
  2. Checked the CloudKit container (Web Console) - all good
  3. Checked that additions/deletions worked correctly whether on the MacMini or via The CloudKit Console - all good.

Somewhat of a pain, but necessary given the amount of data and its complexity. An advantage is that I now have backup and restore functions in the file menu of the MacMini App.

Regards, Michaela

How to fix NSCloudKitMirroringDelegate unhandled exception after faulty model change
 
 
Q