soleil
soleil

Reputation: 13085

Keeping a CoreData NSManagedObject fresh

Imagine a social photo app like Instagram. You see one of your friend's photos in your feed. That photo is persisted in CoreData as something like this:

Photo (NSManagedObject)
 Attributes: id, imageURL
 Relationships: comments (set of Comment objects), likes (set of Like objects)

I have a view controller that has a reference to the Photo object. This view controller also handles actions for liking and commenting on the photo.

So, when someone likes a photo the flow is this: Send the like to the server. If the API post is successful, update the Photo object in CoreData with any new information received from the server, which will include the new like. At this point the Photo should have one more Like object related to it in CoreData than before I pressed the like button.

Now here is where I'm having a problem. The view controller needs to update the number of likes on the photo. In a success block, I'm doing this:

  self.likesLabel.text = [NSString stringWithFormat:@"%d likes", self.photo.likes.count];

The problem is that self.photo.likes.count is reporting 0 at this point (it was non-zero before I pressed the like button). If I navigate elsewhere and come back to this screen, the count will update properly. So it seems that the Photo managedObject becomes "dirty" when I update it. The update probably happens in another context (I use Magical Record). The update looks something like this:

NSManagedObjectContext* context = [NSManagedObjectContext MR_contextForCurrentThread];
Photo* photo = [Photo MR_findFirstWithPredicate:somePredicate inContext:context];
...
photo.likes = likes;
photo.comments = comments;
[photo save:context];

So the question is, how do I properly keep the Photo object updated in the view controller so that I can always reliably query the number of likes and comments? In general, how can you always keep a fresh version of an NSMangagedObject, given that it may be updated in other threads/contexts?

I have tried listening for the NSManagedObjectContextObjectsDidChangeNotification, but I run into the same problem where the Photo reports 0 likes and comments when I get that notification.

Upvotes: 1

Views: 294

Answers (2)

Daij-Djan
Daij-Djan

Reputation: 50099

listen and apply merges made in other contexts

#pragma mark - Core Data stack

- (void)mergeChanges:(NSNotification *)notification {
    // Merge changes into the main context on the main thread
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.mainManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
        [[NSNotificationCenter defaultCenter] postNotificationName:MyMainContextMergedChangesNotification object:nil];
    });
}

after the merge you can refresh the GUI


alternativly/additionally you can force the context to refresh the object from the db calling refreshObject:

Upvotes: 1

casademora
casademora

Reputation: 69687

One solution for this scenario I frequently use is KVO. In your case, I would observe the "likes" property on the managed object. You can look up standard KVO articles on how to do that properly. However, this is only half the story. So, in order for those values to change for you properly, the NSManagedObjectContext that is "managing" that property needs to get updates itself. This is where either merging saves on NSManagedOjbectContextDidSaveNotification events in the case of a thread isolation context, or the parent/child merge rules come into play. The point is, you could be making the change to the actual data on any context within your app. It's up to you to merge in those changes into the context you currently care about (probably what's displaying on the screen).

All that said, MagicalRecord should have some build in methods to already handle that half of the problem for you. If not, make sure your context that is displaying use information is being updated properly when a background context is being saved. The thing to remember is that you have to update the context, and once the context merges in the new changes on update, then your KVO methods will fire with the proper values.

Upvotes: 1

Related Questions