Core Data NSPredicate for relationships doesn't work after [context save]

347 Views Asked by At

I have two entities in Core Data (see below), and using NSFetchedResultsController with [NSPredicate predicateWithFormat:@"calendar.subscribed == 1"]; to extract "Event" object.

Calendar

  • subscribed (BOOL)
  • events (one-to-many relationship to "Event")

Event

  • calendar (many-to-one relationship to "Calendar")

Everything works fine, but if I change subscribed property of some "Calendar" and save the context in other thread, controllerDidChangeContent isn't been called.

can I force to refetch? and how?

3

There are 3 best solutions below

2
On

Sadly the FRC only monitors the objects that are the subject of the fetch (in your case Event). So it does not recognise changes to attributes of related objects, even though those changes might affect how the predicate evaluates.

One workaround (if you can live with the performance hit) is, when you change the Calendar object's attribute value, to change a value on the associated Event object to its existing value - no change in theory, but enough to trigger the FRC to re-evaluate the predicate for that Event.

0
On

As mentioned in the other answer, NSFetchedResultsController is only going to pick up the changes to the Event objects and not the Calendar. To force a refresh, you could post an NSNotification wherever you update a calendar's subscribed status right after you call 'saveContext':

[[NSNotificationCenter defaultCenter] postNotificationName:@"calendarSubscriptionUpdated" object:nil];

Then add an observer in the class you want to update:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateEventData:) name:@"calendarSubscriptionUpdated" object:nil];

Populate an 'updateEventData' (or whatever you want to call it) method with code to run the fetch, and you should be good to go.

0
On

You can add observer to Calendar.subscribed in the Event

-(void)awakeFromFetch {
  [ self addObserver:self
            forKeyPath:@"calendar.subscribed"
               options:NSKeyValueObservingOptionNew
               context:NULL ];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"calendar.subscribed"]) {
        [ self willChangeValueForKey:@"anyProperty" ];
        [ self didChangeValueForKey:@"anyProperty" ];
    }
}

-(void)willTurnIntoFault {
    [ super willTurnIntoFault];
    [ self removeObserver:self forKeyPath:@"calendar.subscribed" ];
}

Instead of "anyProperty" use real property's name of the Event. I've used the same approach before it works.