3
votes

There are a lot of questions on this topic but perhaps all too specific and none too concise.

I have a NSFetchedResultsController. It initially fetches the data I need. When I update the data model which would affect the results of the NSPredicate of the NSFetchRequest, the content does not update.

More concretely, I have a Permissions model. There are data objects that are assigned permissions, then there are users who have a subset of these permissions, though the data object's Permission is not the same as the User's permission; they do share the same controlKey.

So, the NSPredicate is:

[NSPredicate predicateWithFormat:@"controlKey IN %@", [[User currentUser].permissions valueForKey:@"controlKey"]];

The problem is, if I create and add a permission, the content does not update when I save the NSManagedObjectContext (yes, I'm observing it. No, I'm not using a cache so nothing to delete).

I'm pretty convinced it's because of the predicate. It's still the same array as initially, and doesn't get updated.

My question is, how do I write a predicate that gets me what I want but still remains "dynamic" ? It would be nice to have UITableView animations as this object is added.

3
did you implemented the NSFetchedResultsController delegate? - Eyal
yes I did. Not a problem there. - horseshoe7

3 Answers

0
votes

I have tried with following code snippet and it works for me:

[NSFetchedResultsController deleteCacheWithName:nil];  
[self.fetchedResultsController.fetchRequest setPredicate:predicate];  // set your new predicate

NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
    NSLog(@"%@, %@", error, [error userInfo]);
    abort();
} 
2
votes

Here is what you need to do. When detecting the change,

self.fetchedResultsController.predicate = //new predicate
[self.fetchedResultsController performFetch:nil];
[self.tableView reloadData];

Or sometimes it is necessary to wipe the FRC clean.

_predicate = // new predicate, put it into an ivar
self.fetchedResultsController = nil;
[self.tableView reloadData];  // lazily re-instantiate FRC with _predicate

This could be expensive, but in order to get animations, you could try replacing reloadData with the following:

[self.tableView beginUpdates];
[self.tableView endUpdates];
1
votes

You query the control keys from a set of permissions and pass them to the predicate. The predicate itself isn't dynamic anyway, it is only the result of the fetch which is dynamic. So, you can't do exactly what you want.

Instead, you should be observing the permissions change for the user, creating a new predicate, updating the FRC and re-executing it (a requirement after you change the predicate, because it is expected to be static).