27

I am presenting table view contents using NSFetchedResultsController which has a predicate:

[NSPredicate predicateWithFormat:@"visible == %@", [NSNumber numberWithBool:YES]]

On background thread using separate NSManagedObjectContext I update few of the entities and change theirs visible value from NO to YES. Save, merge changes in main thread's NSManagedObjectContext. But NSFetchedResultsController's fetchedObjects doesn't change. Also controller doesn't call -controller:didChangeObject:... on delegate. If entities are updated on main thread in identical manner (my test app calls the same method), everything works as expected.

Also Notification's NSUpdatedObjectsKey contains those objects.

Currently the only solutions I've found is to call for each of NSUpdatedObjectsKey entities:

NSManagedObjectContext *context = ... // main thread context
[context existingObjectWithID:[object objectID] error:nil]

This issue is only with updated objects which previously didn't match the predicate.

Am I missing something obvious?

Lorenzo B
  • 33,216
  • 24
  • 116
  • 190
ampatspell
  • 844
  • 7
  • 10

2 Answers2

55

Turns out main NSManagedObjectContext didn' t event fire NSManagedObjectContextObjectsDidChangeNotification for updated objects because it is not done for faulted objects.

Generic fix (or keep a track of object IDs that needs this treatment):

NSManagedObjectContext *context = [self managedObjectContext];
for(NSManagedObject *object in [[notification userInfo] objectForKey:NSUpdatedObjectsKey]) {
  [[context objectWithID:[object objectID]] willAccessValueForKey:nil];
}

[context mergeChangesFromContextDidSaveNotification:notification];

From NSManagedObject Class Reference:

You can invoke this method with the key value of nil to ensure that a fault has been fired, as illustrated by the following example.

ampatspell
  • 844
  • 7
  • 10
  • This saved me so much time. I cannot upvote this enough. Thank you. – Jesse Rusak Mar 14 '11 at 22:47
  • Wow same for me. Been wasting hours on this! – samvermette Jul 19 '11 at 00:33
  • Shouldn't there be a way to do this with different settings on the `NSFetchRequest`? I tried specifying `relationshipsForPrefetching` and -[NSFetchRequest setReturnsObjectAsFaults:NO]`, but neither worked. – Heath Borders Dec 21 '12 at 20:33
  • Also, this didn't work for me. I have basically the exact same scenario you do. – Heath Borders Dec 21 '12 at 20:38
  • 1
    Sounds like you are doing the merge on the background thread. The merge of changes needs to be done on the main/UI thread for the events to fire properly. This is moot though if you are using parent/child MOCs as they handle these notifications internally. – Marcus S. Zarra Dec 22 '12 at 13:39
  • 1
    Nope, all of my changes and merges happen on the main thread. I'll create a sample project tomorrow with a radar. – Heath Borders Dec 23 '12 at 08:03
  • Nevermind. I swear this didn't work on my project the first time I tried it, but I just tried it again, and it worked. Sorry. – Heath Borders Dec 25 '12 at 04:28
  • 2
    Using MagicalRecord, and I seem to be running into the same problem. However the fix above did not work for me. I do have my FRC on a background context (thread) and I am not seeing changes. – lostintranslation Dec 30 '14 at 03:53
  • @lostintranslation Did you find solution with MagicalRecord? – ChikabuZ May 29 '15 at 14:00
  • This was in fact a bug that Apple fixed a few years ago. The question isn't relevant anymore (2016 onwards). – strangetimes Nov 19 '19 at 12:06
1

You have to call processPendingChanges on your Background-NSManagedObjectContext after you merged the changes from an other NSManagedObjectContext.

See CoreData Programming Guide:

Note that the change notification is sent in NSManagedObjectContext’s processPendingChanges method. The main thread is tied into the event cycle for the application so that processPendingChanges is invoked automatically after every user event on contexts owned by the main thread. This is not the case for background threads—when the method is invoked depends on both the platform and the release version, so you should not rely on particular timing. If the secondary context is not on the main thread, you should call processPendingChanges yourself at appropriate junctures.

Artur Friesen
  • 155
  • 1
  • 1
  • 9
  • Are you sure this would fix the issue above? I don't think the NSFetchedResults controller is listening for change notifications from the *background* context. – Jesse Rusak Oct 02 '13 at 00:52
  • `mergeChangesFromContextDidSaveNotification:` performs (almost) the same process as `processPendingChanges`, and posts the appropriate notification. – quellish Sep 17 '14 at 05:54
  • It's not that `mergeChangesFromContextDidSaveNotification` performs the same process as `processPendingChanges`, it's the fact that `processPendingChanges` gets called as part of the merge. `processPendingChanges` is guaranteed to be called by the end of each runloop (at least on macOS) or each time a context is saved. It also gets called as part of merge. – strangetimes Nov 19 '19 at 12:04