RESTKit: Comparing GET object with locally persist

2019-05-25 01:57发布

问题:

I have a saved object (persisted) in Core Data. Lets say below are the values:

//Entity: employee
objectID: 1111
firstName: @"Jon"
lastName: @"D"
modified: @"10:45PM"

Now, I do a RKManagedObjectRequestOperation *operation request.

I set operation.savesToPersistentStore = NO; and start the operation.

The object was downloaded and it's been modified to the following:

//Entity: employee
objectID: 1111
firstName: @"Jonathan"
lastName: @"Doe"
modified: @"10:55PM"
//I have 15 other properties that are either Strings, NSDate, NSNumber, and BOOLs

I believe at this time the modified object is in the managedObjectContext

If the modified date is different in the Context, I would like to compare each attribute in the Context with the one that's persisted in Core Data. And if the Context attribute is different than the one in persisted, I need to trigger a flag for each attribute property. Once all the attributes have been chacked, I can overwrite the object by saving the Context.

I'm thinking I'll have to manually check:

if (!([objectInCoreData valueForKey:@"firstName"] isEqualToString:[objectInContext valueForKey@"firstName"])) 
{ 
     firstNameValueChange = YES; // Trigger this flag
     // I have to trigger a flag for each attribute that's changed to show 
     // the changed value in the tableView in a different color
}

//Continue to check for each attribute, and at end overwrite persisted with Context by
[self.managedObjectContext save:&error];

Question 1: I have a feeling there's a better way to do this. What's the best practice?
How can I simplify the code for checking 15 different attributes?

Question 2: Where do I test if each attribute has changed? inside the successBlock? wilSave: method?

** UPDATE *

RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc]initWithRequest:request responseDescriptors:@[responseDescriptor]];
operation.managedObjectContext = self.objectManager.managedObjectStore.mainQueueManagedObjectContext;
operation.managedObjectCache = appDelegate.managedObjectStore.managedObjectCache;
operation.savesToPersistentStore = NO;


[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {

    NSSet *updatedObjects = [self.managedObjectContext updatedObjects];
    NSLog(@"updatedObjects Count %i", updatedObjects.count);

    NSArray *_updatedObjects = updatedObjects.allObjects;
    NSLog(@"_updatedObjects Count %i", _updatedObjects.count);

    for (int i = 0; i<_updatedObjects.count; i++)
    {
        Invite *invite = [_updatedObjects objectAtIndex:i];
        NSDictionary *changedValues = [invite changedValues];            
    }
}failure:^(RKObjectRequestOperation *operation, NSError *error) {}

Log

 I restkit.network:RKObjectRequestOperation.m:250 GET 'http://www.domain.com/api?lastrequest=2014-04-22T07%3A06%3A34Z' (200 OK / 28 objects) [request=0.1140s mapping=0.0865s total=0.3034s]
2014-04-29 07:06:43.112 App[10627:60b] updatedObjects Count 0
2014-04-29 07:06:43.112 App[10627:60b] _updatedObjects Count 0

I can see the log that I'm getting a few objects back, but everything is null or 0. I guess my MOC is blank. How do the objects go from mappingResult to MOC?

回答1:

The below option doesn't work correctly because the MOC is saved even if the save isn't propagated up to the persistent store so you can't ask it about its changes. You could use multiple contexts with the same approach, but:

RestKit is able to use KVC validation. As such you can implement a number of methods on your model objects and add your validation logic. These methods are called with the new incoming data and have access to the current data (the instance variables of self) so you can check the incoming value and check if you want to:

  1. accept it
  2. change it
  3. abort the mapping altogether

See this ref.


From the MOC you can get updatedObjects. Once you have the updated objects you can get the changedValues for each. Now, using the keys from that dictionary, you can get the previous saved values with committedValuesForKeys:.

Now you have all the information required to make your comparison. You should process each of the keys and apply whatever logic you want. As part of the processing you can change some variables back to their saved values, or reset the entire object using refreshObject:mergeChanges:.

At the end of your processing, save the context.

You should not be using multiple different contexts to achieve this, though you should probably be using a different context from the standard main queue context.