drewmccormack / ensembles

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

Detect changes coming from merge #236

Closed pronebird closed 8 years ago

pronebird commented 8 years ago

Hi,

Is there any reliable way to capture changes coming after sync. I specifically want to find out which objects and which properties were changed during sync.

I hooked up both CDEMonitoredManagedObjectContextWillSaveNotification and CDEMonitoredManagedObjectContextDidSaveNotification but CDEMonitoredManagedObjectContextDidSaveNotification is not always called.

I put the breakpoint in contextDidSave and it is not fired most of the time, however willSave is always fired.

i.e.

NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];

[notificationCenter addObserver:self
                       selector:@selector(contextWillSave:)
                           name:CDEMonitoredManagedObjectContextWillSaveNotification
                         object:nil];

[notificationCenter addObserver:self
                       selector:@selector(contextDidSave:)
                           name:CDEMonitoredManagedObjectContextDidSaveNotification
                         object:nil];

- (void)contextWillSave:(NSNotification *)note {
    if(![self _shouldReportChanges]) {
        return;
    }

    CDEPersistentStoreEnsemble *ensemble = [note.userInfo objectForKey:@"persistentStoreEnsemble"];
    NSManagedObjectContext *context = [note object];

    if(self.cdStack.ensemble != ensemble) {
        return;
    }

    NSMutableDictionary *changes = [self.changesByKey mutableCopy];

    NSLog(@"changes: %@", changes);

    [[context updatedObjects] enumerateObjectsUsingBlock:^(NSManagedObject *object, __unused BOOL *stop) {
        if([object.entity.name isEqualToString:[OptionModel entityName]]) {
            NSString *optionKey = [object valueForKey:OptionModelAttributes.name];
            NSDictionary *oldValues = [object committedValuesForKeys:@[ OptionModelAttributes.value ]];
            NSDictionary *newValues = [object changedValues];

            // ....
        }
    }];

    self.changesByKey = [changes copy];
}

- (void)contextDidSave:(NSNotification *)note {
    if(![self _shouldReportChanges]) {
        return;
    }

    CDEPersistentStoreEnsemble *ensemble = [note.userInfo objectForKey:@"persistentStoreEnsemble"];    
    if(self.cdStack.ensemble != ensemble) {
        return;
    }

    NSDictionary *changedKeys = [self.changesByKey copy];

    // ...
}
drewmccormack commented 8 years ago

Those notifications are fired when your context saves, not the Ensembles context.

There are delegate methods on CDEPersistentStoreEnsemble that can be used to get what changed. Look for persistentStoreEnemble:didSaveMerge…

Drew

On 11 Jul 2016, at 12:42, Andrei Mihailov notifications@github.com wrote:

Hi,

Is there any reliable way to capture changes coming after sync. I specifically want to find out which objects and which properties were changed during sync.

I hooked up both CDEMonitoredManagedObjectContextWillSaveNotification and CDEMonitoredManagedObjectContextDidSaveNotification but CDEMonitoredManagedObjectContextDidSaveNotification is not always called.

i.e.

NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];

[notificationCenter addObserver:self
                       selector:@selector(contextWillSave:)
                           name:CDEMonitoredManagedObjectContextWillSaveNotification
                         object:nil];

[notificationCenter addObserver:self
                       selector:@selector(contextDidSave:)
                           name:CDEMonitoredManagedObjectContextDidSaveNotification
                         object:nil];
  • (void)contextWillSave:(NSNotification *)note { if(![self _shouldReportChanges]) { return; }

    CDEPersistentStoreEnsemble ensemble = [note.userInfo objectForKey:@"persistentStoreEnsemble"]; NSManagedObjectContext context = [note object];

    if(self.cdStack.ensemble != ensemble) { return; }

    NSMutableDictionary *changes = [self.changesByKey mutableCopy];

    NSLog(@"changes: %@", changes);

    [[context updatedObjects] enumerateObjectsUsingBlock:^(NSManagedObject object, __unused BOOL stop) { if([object.entity.name isEqualToString:[OptionModel entityName]]) { NSString optionKey = [object valueForKey:OptionModelAttributes.name]; NSDictionary oldValues = [object committedValuesForKeys:@[ OptionModelAttributes.value ]]; NSDictionary *newValues = [object changedValues];

      // ....
    }

    }];

    self.changesByKey = [changes copy]; }

  • (void)contextDidSave:(NSNotification *)note { if(![self _shouldReportChanges]) { return; }

    CDEPersistentStoreEnsemble *ensemble = [note.userInfo objectForKey:@"persistentStoreEnsemble"];
    if(self.cdStack.ensemble != ensemble) { return; }

    NSDictionary *changedKeys = [self.changesByKey copy];

    // ... } — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/drewmccormack/ensembles/issues/236, or mute the thread https://github.com/notifications/unsubscribe/AAEuAPYgZSR1-4e8vkLpLIm_zC40hRA-ks5qUh4mgaJpZM4JJPXZ.

pronebird commented 8 years ago

Ah now it makes sense. Since both reparation and saving contexts are passed during merge, is it legit to call save on reparation context after de-duplication to get all of changes pop to saving context so I could capture all of the changes? Or Ensembles expects reparation context to be unsaved?

drewmccormack commented 8 years ago

You should not save any of the contexts. Ensembles uses the unsaved state to create data for syncing. It has to be able to go through the changes in the reparation context before saving it.

If you just care about what changed overall, use the did-save delegate method. The should-save one, with the reparation context, has changes spread over contexts, and the changes are not saved.

Drew

On 11 Jul 2016, at 13:11, Andrei Mihailov notifications@github.com wrote:

Ah now it makes sense. Since both reparation and saving contexts are passed during merge, is it legit to call save on reparation context to get all of changes pop to saving context so I could capture all of the changes? Or Ensembles expects reparation context to be unsaved?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/drewmccormack/ensembles/issues/236#issuecomment-231706216, or mute the thread https://github.com/notifications/unsubscribe/AAEuANrf7J86XPpuskRZMx_SQCBN3gwXks5qUiTkgaJpZM4JJPXZ.

pronebird commented 8 years ago

I need to capture old value for specific entity, so I can make a proper transition in business logic of the app. I guess I will have to go through root and then reparation contexts to collect updates.

drewmccormack commented 8 years ago

Are you actually making changes in the reparation context? If you don’t make any, all the changes will be in the saving context.

Drew

On 11 Jul 2016, at 13:24, Andrei Mihailov notifications@github.com wrote:

I need to capture old value for specific entity, so I can make a proper transition in business logic of the app. I guess I will have to go through root and then reparation contexts to collect updates.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/drewmccormack/ensembles/issues/236#issuecomment-231708424, or mute the thread https://github.com/notifications/unsubscribe/AAEuAK7AWhr8804vOO6ebGorWI7rJfGYks5qUifogaJpZM4JJPXZ.

pronebird commented 8 years ago

@drewmccormack I do de-duplication in reparation context. My theory is, I can collect updated objects by going through savingContext changes first then through reparationContext changes. I can probably make use of NSSet's intersection method to exclude deleted objects during reparation.

pronebird commented 8 years ago

@drewmccormack thank you for all your suggestions. Is there any particular reason why CDEMonitoredManagedObjectContextDidSaveNotification hides the normal userInfo keys that usually come with NSManagedObjectContextDidSave such as NSInsertedObjectsKey, NSUpdatedObjectsKey, and NSDeletedObjectsKey?

Trying to make use of Monitored context notifications to avoid colliding with Ensembles. Technically I can obtain all of that from context in CDEMonitoredManagedObjectContextWillSaveNotification. Just felt a bit weird that notifications that are supposed to be used in place of standard NSManagedObjectContext notifications hide the data that I would usually encounter in CoreData notifications...

pronebird commented 8 years ago

Closing this issue because delegate provides sufficient functionality to capture changes coming from other device.

pronebird commented 8 years ago

It's better to hook up NSManagedObjectContextDidSave of reparation context from shouldSaveMergedChangesInManagedObjectContext and once it happens, grab changes from saving context. Ensembles saves reparation context shortly after shouldSaveMergedChangesInManagedObjectContext. This way I know all changes coming from Ensembles including any deduplication happening in reparation context.