drewmccormack / ensembles

A synchronization framework for Core Data.
MIT License
1.63k stars 132 forks source link

Crash in persistentStoreEnsemble: globalIdentifiersForManagedObjects: - uniqueIdentifier - entity is not KeyValueCoding compliant #289

Open dgwilson opened 4 years ago

dgwilson commented 4 years ago

I have a macOS desktop app and an iOS app that I wish to sync the data between. They are apps that already existing their respective app stores. And I'm trying to switch to ensembles free.

In the macOS app I'm getting this crash (ensembles free was downloaded about the beginning of June 2020). I think I've followed all of the instructions for integration... wouldn't surprise me if I missed something.

The crash will happen on first run, not on the second. Then crash on the 3rd (same crash point/error) and not on the forth. Screenshot of the crash is below. Zombie objects is also enabled.

Screen Shot 2020-06-13 at 11 27 34 AM

Ultimately I'm not sure if I should load my existing data and migrate to a new store? I'm don't know what the correct approach is. First thing is to get past this crash though.

drewmccormack commented 4 years ago

It looks like you probably don’t have a uniqueIdentifier property on all of your entities. You need to have something that identifies objects globally. If you do, the Ensembles can do all the import etc.

So figure out if you already have such a property and return that in the delegate method. If not, see the model chapter of the book for strategies to introduce such a property in preexisting data.

Kind regards, Drew

dgwilson commented 4 years ago

Thanks for the quick reply Drew.

I’ve been reading the book and holding off any changes I might make pending your reply.

I do not have a key that is “uniqueIdentifier”… but may have something else that will do the job… although it’s nothing like a UUID as per the text in your book. Assuming I have something that might work… should I change the line of code

to be

or do I have to have a field called uniqueIdentifier?

Also… regarding the uniqueIdentifier… if I follow the book it shows an example of UUID which is unique to the machine… but on it’s own it’s a duplicate with the next entry… what I’m asking is that that it doesn’t need to be UUID plus date/time to make it really really unique… if you see what I mean.

I have more questions.. about “Options” for the persistent store and how to “baseline”… but I don’t want to complicate this thread at the moment.

Thanks again for your fast reply.

On 13/06/2020, at 10:03 PM, Drew McCormack notifications@github.com wrote:

It looks like you probably don’t have a uniqueIdentifier property on all of your entities. You need to have something that identifies objects globally. If you do, the Ensembles can do all the import etc.

So figure out if you already have such a property and return that in the delegate method. If not, see the model chapter of the book for strategies to introduce such a property in preexisting data.

Kind regards, Drew

David Wilson http://dgwilson.wordpress.com https://nz.linkedin.com/in/dgwilson65

drewmccormack commented 4 years ago

Yes, you can change the code to return whatever your unique id is. It doesn’t have to be called uniqueIdentifier

It is very important that the unique id of an object be unique, and be immutable. It can’t change. And the same object on different devices should have that same id, so they match up.

A creation date is a possible unique I’d, but there is a good chance it will collide with other ids, breaking the uniqueness requirement.

Kind regards, Drew

On 13 Jun 2020, at 12:15, David Wilson notifications@github.com wrote:

 Thanks for the quick reply Drew.

I’ve been reading the book and holding off any changes I might make pending your reply.

I do not have a key that is “uniqueIdentifier”… but may have something else that will do the job… although it’s nothing like a UUID as per the text in your book. Assuming I have something that might work… should I change the line of code

  • (NSArray )persistentStoreEnsemble:(CDEPersistentStoreEnsemble )ensemble globalIdentifiersForManagedObjects:(NSArray *)objects { return [objects valueForKeyPath:@"uniqueIdentifier"]; }

to be

  • (NSArray )persistentStoreEnsemble:(CDEPersistentStoreEnsemble )ensemble globalIdentifiersForManagedObjects:(NSArray *)objects { return [objects valueForKeyPath:@“myUniqueIdentifier"]; }

or do I have to have a field called uniqueIdentifier?

Also… regarding the uniqueIdentifier… if I follow the book it shows an example of UUID which is unique to the machine… but on it’s own it’s a duplicate with the next entry… what I’m asking is that that it doesn’t need to be UUID plus date/time to make it really really unique… if you see what I mean.

I have more questions.. about “Options” for the persistent store and how to “baseline”… but I don’t want to complicate this thread at the moment.

Thanks again for your fast reply.

  • David

On 13/06/2020, at 10:03 PM, Drew McCormack notifications@github.com wrote:

It looks like you probably don’t have a uniqueIdentifier property on all of your entities. You need to have something that identifies objects globally. If you do, the Ensembles can do all the import etc.

So figure out if you already have such a property and return that in the delegate method. If not, see the model chapter of the book for strategies to introduce such a property in preexisting data.

Kind regards, Drew

David Wilson http://dgwilson.wordpress.com https://nz.linkedin.com/in/dgwilson65

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

dgwilson commented 4 years ago

Thank you for the feedback Drew. I’ve done my updates and have read the section in the book on UUID and understand. I may make further changes but I get it.

New TOPIC if you will…

My existing macOS app and iOS app attempted to sync via iCloud. Now with the installation of Ensemble do I turn off iCloud syncing? eeerrrr How?

I have this configuration for the persistentStore:

        options = @{NSMigratePersistentStoresAutomaticallyOption: @YES,                    // Key to automatically attempt to migrate versioned stores
                    NSInferMappingModelAutomaticallyOption: @YES,                          // Key to attempt to create the mapping model automatically
                    NSPersistentStoreUbiquitousContentNameKey: @"TrafficCamNZ_DataStore",  // Option to specify that a persistent store has a given name in ubiquity.
                    NSPersistentStoreUbiquitousContentURLKey: icloudURLForLogs             // Option to specify the log path to use for ubiquitous content logs.
                    };

I had tried removing the UbiquitousContent keys… and ran into all sorts of issues.

On 14/06/2020, at 1:52 AM, Drew McCormack notifications@github.com wrote:

Yes, you can change the code to return whatever your unique id is. It doesn’t have to be called uniqueIdentifier

It is very important that the unique id of an object be unique, and be immutable. It can’t change. And the same object on different devices should have that same id, so they match up.

A creation date is a possible unique I’d, but there is a good chance it will collide with other ids, breaking the uniqueness requirement.

Kind regards, Drew

On 13 Jun 2020, at 12:15, David Wilson notifications@github.com wrote:

 Thanks for the quick reply Drew.

I’ve been reading the book and holding off any changes I might make pending your reply.

I do not have a key that is “uniqueIdentifier”… but may have something else that will do the job… although it’s nothing like a UUID as per the text in your book. Assuming I have something that might work… should I change the line of code

  • (NSArray )persistentStoreEnsemble:(CDEPersistentStoreEnsemble )ensemble globalIdentifiersForManagedObjects:(NSArray *)objects { return [objects valueForKeyPath:@"uniqueIdentifier"]; }

to be

  • (NSArray )persistentStoreEnsemble:(CDEPersistentStoreEnsemble )ensemble globalIdentifiersForManagedObjects:(NSArray *)objects { return [objects valueForKeyPath:@“myUniqueIdentifier"]; }

or do I have to have a field called uniqueIdentifier?

Also… regarding the uniqueIdentifier… if I follow the book it shows an example of UUID which is unique to the machine… but on it’s own it’s a duplicate with the next entry… what I’m asking is that that it doesn’t need to be UUID plus date/time to make it really really unique… if you see what I mean.

I have more questions.. about “Options” for the persistent store and how to “baseline”… but I don’t want to complicate this thread at the moment.

Thanks again for your fast reply.

  • David

On 13/06/2020, at 10:03 PM, Drew McCormack notifications@github.com wrote:

It looks like you probably don’t have a uniqueIdentifier property on all of your entities. You need to have something that identifies objects globally. If you do, the Ensembles can do all the import etc.

So figure out if you already have such a property and return that in the delegate method. If not, see the model chapter of the book for strategies to introduce such a property in preexisting data.

Kind regards, Drew

David Wilson http://dgwilson.wordpress.com https://nz.linkedin.com/in/dgwilson65

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/drewmccormack/ensembles/issues/289#issuecomment-643626748, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABVCN6RLKPJDREG5TEJ7ODRWOABDANCNFSM4N4W4W6A.

David Wilson http://dgwilson.wordpress.com https://nz.linkedin.com/in/dgwilson65

drewmccormack commented 4 years ago

To "turn off" sync, simply don't call the leech or merge methods. Ensembles does not sync for you automatically. It is up to you to say when you want to sync. If you never call either of those methods, you will never sync.

To remove the local cache of data and "detach" from the cloud, call the deleech method.

If you want to remove all data, call deleech first, and in the completion handler, call the class method +removeEnsemble... passing in the file system object.

Kind regards, Drew

dgwilson commented 4 years ago

Thanks again for your reply.

I wanted to know how to turn off the iCloud syncing I had implemented (the standard Apple one). I might be asking the wrong question here I don’t know. … so I had implemented Apple’s iCloud syncing for CoreData - at least I thought I had … and now I’ve implemented ensembles syncing

so I have now have 2 x sync’s running?

Hoping that question makes sense…

On 15/06/2020, at 7:14 PM, Drew McCormack notifications@github.com wrote:

To "turn off" sync, simply don't call the leech or merge methods. Ensembles does not sync for you automatically. It is up to you to say when you want to sync. If you never call either of those methods, you will never sync.

To remove the local cache of data and "detach" from the cloud, call the deleech method.

If you want to remove all data, call deleech first, and in the completion handler, call the class method +removeEnsemble... passing in the file system object.

Kind regards, Drew

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/drewmccormack/ensembles/issues/289#issuecomment-643948430, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABVCN6JHSLR2TYBUDHOUETRWXC6PANCNFSM4N4W4W6A.

drewmccormack commented 4 years ago

Ensembles doesn't use or need any of the Apple Core Data sync. (Which one were you using: the old one or the new one from last year?)

It could be that to stop using Apple's 'old' solution, you have to migrate your store. See this article, in particular under the heading (iCloud on/Off Switch). https://www.objc.io/issues/10-syncing-data/icloud-core-data/

dgwilson commented 4 years ago

Hi Drew…

Good news. I've made progress. Thank you for your guidance.

A new issue has come up. I’m getting this error on iOS and macOS.

Thread 21: Exception: "Failed to process pending changes before save. The context is still dirty after 1000 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler."

I do have a willSave procedure in the main entity. Which I’ve also pasted below if it helps provide any clues as to what is going on.

On 16/06/2020, at 8:09 PM, Drew McCormack notifications@github.com wrote:

Ensembles doesn't use or need any of the Apple Core Data sync. (Which one were you using: the old one or the new one from last year?)

It could be that to stop using Apple's 'old' solution, you have to migrate your store. See this article, in particular under the heading (iCloud on/Off Switch). https://www.objc.io/issues/10-syncing-data/icloud-core-data/ https://www.objc.io/issues/10-syncing-data/icloud-core-data/ — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/drewmccormack/ensembles/issues/289#issuecomment-644606772, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABVCNY7MKRNEDEYZPL7OKDRW4SDZANCNFSM4N4W4W6A.

David Wilson http://dgwilson.wordpress.com https://nz.linkedin.com/in/dgwilson65

drewmccormack commented 4 years ago

You could think about moving those earlier in the lifecycle, but if they are necessary, at least make sure each one is called just once. If you call a setter or fire KVO in willSave, it triggers a new save. This can go on forever. So you have to make sure that if you do this, the second call will go through without doing anything. No setter call or KVO. You can use an if to determine that.

Kind regards, Drew

On 21 Jun 2020, at 02:40, David Wilson notifications@github.com wrote:

 Hi Drew…

Good news. I've made progress. Thank you for your guidance.

A new issue has come up. I’m getting this error on iOS and macOS.

Thread 21: Exception: "Failed to process pending changes before save. The context is still dirty after 1000 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler."

I do have a willSave procedure in the main entity. Which I’ve also pasted below if it helps provide any clues as to what is going on.

  • (void)willSave { [super willSave]; [self updateRecordUUID]; }

  • (void)updateRecordUUID { // NSLog(@"%@", NSStringFromSelector(_cmd)); if (![self.recordUUID isEqualToString:self.url]) { if (self.url) { [self willChangeValueForKey:@"recordUUID"]; [self setPrimitiveValue:self.url forKey:@"recordUUID"]; [self didChangeValueForKey:@"recordUUID"]; } else { [self willChangeValueForKey:@"recordUUID"]; [self setPrimitiveValue:[[NSUUID UUID] UUIDString] forKey:@"recordUUID"]; [self didChangeValueForKey:@"recordUUID"]; } } }

  • David

On 16/06/2020, at 8:09 PM, Drew McCormack notifications@github.com wrote:

Ensembles doesn't use or need any of the Apple Core Data sync. (Which one were you using: the old one or the new one from last year?)

It could be that to stop using Apple's 'old' solution, you have to migrate your store. See this article, in particular under the heading (iCloud on/Off Switch). https://www.objc.io/issues/10-syncing-data/icloud-core-data/ https://www.objc.io/issues/10-syncing-data/icloud-core-data/ — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/drewmccormack/ensembles/issues/289#issuecomment-644606772, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABVCNY7MKRNEDEYZPL7OKDRW4SDZANCNFSM4N4W4W6A.

David Wilson http://dgwilson.wordpress.com https://nz.linkedin.com/in/dgwilson65

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

dgwilson commented 4 years ago

Thank you for the advice Drew.

I’ve modified the implementation so as not to invoke KVO and things are running under macOS and iOS and the changes are syncing. I’ve also implemented a timer in both apps.

Hopefully you can use some of these questions and answers in your book.

On 21/06/2020, at 8:27 PM, Drew McCormack notifications@github.com wrote:

You could think about moving those earlier in the lifecycle, but if they are necessary, at least make sure each one is called just once. If you call a setter or fire KVO in willSave, it triggers a new save. This can go on forever. So you have to make sure that if you do this, the second call will go through without doing anything. No setter call or KVO. You can use an if to determine that.

Kind regards, Drew

On 21 Jun 2020, at 02:40, David Wilson notifications@github.com wrote:

 Hi Drew…

Good news. I've made progress. Thank you for your guidance.

A new issue has come up. I’m getting this error on iOS and macOS.

Thread 21: Exception: "Failed to process pending changes before save. The context is still dirty after 1000 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler."

I do have a willSave procedure in the main entity. Which I’ve also pasted below if it helps provide any clues as to what is going on.

  • (void)willSave { [super willSave]; [self updateRecordUUID]; }

  • (void)updateRecordUUID { // NSLog(@"%@", NSStringFromSelector(_cmd)); if (![self.recordUUID isEqualToString:self.url]) { if (self.url) { [self willChangeValueForKey:@"recordUUID"]; [self setPrimitiveValue:self.url forKey:@"recordUUID"]; [self didChangeValueForKey:@"recordUUID"]; } else { [self willChangeValueForKey:@"recordUUID"]; [self setPrimitiveValue:[[NSUUID UUID] UUIDString] forKey:@"recordUUID"]; [self didChangeValueForKey:@"recordUUID"]; } } }

  • David

On 16/06/2020, at 8:09 PM, Drew McCormack notifications@github.com wrote:

Ensembles doesn't use or need any of the Apple Core Data sync. (Which one were you using: the old one or the new one from last year?)

It could be that to stop using Apple's 'old' solution, you have to migrate your store. See this article, in particular under the heading (iCloud on/Off Switch). https://www.objc.io/issues/10-syncing-data/icloud-core-data/ https://www.objc.io/issues/10-syncing-data/icloud-core-data/ — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/drewmccormack/ensembles/issues/289#issuecomment-644606772, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABVCNY7MKRNEDEYZPL7OKDRW4SDZANCNFSM4N4W4W6A.

David Wilson http://dgwilson.wordpress.com https://nz.linkedin.com/in/dgwilson65

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe. — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/drewmccormack/ensembles/issues/289#issuecomment-647096989, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABVCN4YP5456D4XY3WBOT3RXXAAXANCNFSM4N4W4W6A.

David Wilson http://dgwilson.wordpress.com https://nz.linkedin.com/in/dgwilson65

dgwilson commented 3 years ago

Hi Drew...

Good news things seem to be going well... still in development between macOS and iOS (actual iPad and occasional simulator session).

I did at one stage remove the iPad application and delete all the data (via the desktop application). The began with a fresh data import which also loaded UUID's as - [[NSUUID UUID] UUIDString] - for all rows.

Now I'm getting new rows occasionally inserted... They have a UUID and the rest of the data is blank...

How can I figure out where these are coming from and fix or prevent it?

drewmccormack commented 3 years ago

Sounds like you perhaps changed the core data store (Eg deleted it) without deleeching.

Best to

Kind regards, Drew

On 1 Aug 2020, at 09:55, David Wilson notifications@github.com wrote:

 Hi Drew...

Good news things seem to be going well... still in development between macOS and iOS (actual iPad and occasional simulator session).

I did at one stage remove the iPad application and delete all the data (via the desktop application). The began with a fresh data import which also loaded UUID's as - [[NSUUID UUID] UUIDString] - for all rows.

Now I'm getting new rows occasionally inserted... They have a UUID and the rest of the data is blank...

How can I figure out where these are coming from and fix or prevent it?

David — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

dgwilson commented 3 years ago

Funny story... I did the coding for that last night for the macOS version... I'll see if I can get iOS done tonight - real life work has got in the way.

If I've interpreted the instructions right... deleech on both (or all) devices... with 10 minutes and then initiate the leech and merge process.

ohhh wait one... I haven't done this... "remove cloud data using the CDEPersistentStoreEnsemble class method removeEnsemble..." ... I'll add that.

Thank you again for the assistance.

dgwilson commented 3 years ago

Good news. In my testing last night between macOS and iPadOS everything worked as it should have. Confirming I did the deleech, removal of cloud data and leech and merge as per doc with 10 minutes elapsed between actions. Now with this new knowledge and experience I can review first time data migration/conversion and the user experience.

Thank you again for the support.