NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext
Asked Answered
H

2

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?

Hamachi answered 13/10, 2010 at 12:53 Comment(3)
Isn't it a bug of Core Data? I think we should fire bug report to Apple. I just did one.Lesson
Bug? If so any progress?Truditrudie
This bug is finally fixed in iOS 10: twitter.com/an0/status/750413478620491776Lesson
H
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.

Hamachi answered 13/10, 2010 at 20:39 Comment(10)
This saved me so much time. I cannot upvote this enough. Thank you.Jessee
Wow same for me. Been wasting hours on this!Count
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.Shirleneshirley
Also, this didn't work for me. I have basically the exact same scenario you do.Shirleneshirley
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.Linsang
Nope, all of my changes and merges happen on the main thread. I'll create a sample project tomorrow with a radar.Shirleneshirley
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.Shirleneshirley
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.Truditrudie
@Truditrudie Did you find solution with MagicalRecord?Rosecan
This was in fact a bug that Apple fixed a few years ago. The question isn't relevant anymore (2016 onwards).Grogram
F
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.

Fleshy answered 1/10, 2013 at 16:12 Comment(3)
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.Jessee
mergeChangesFromContextDidSaveNotification: performs (almost) the same process as processPendingChanges, and posts the appropriate notification.Concentration
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.Grogram

© 2022 - 2024 — McMap. All rights reserved.