soleil
soleil

Reputation: 13073

NSFetchedResultsController not refreshing properly after setting new predicate

My fetchedResultsController is initially created like this (in viewDidLoad):

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
                               entityForName:@"Cat" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];

NSSortDescriptor *sort = [[NSSortDescriptor alloc]
                          initWithKey:@"age" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                    managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"age"
                                               cacheName:@"whatever"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;

This works fine, and results are shown in my table view. However, when pressing certain buttons I want to filter the results. So I do this:

NSPredicate *predicate = ...; //based on buttons pressed


[[self.fetchedResultsController fetchRequest] setPredicate:predicate];
[NSFetchedResultsController deleteCacheWithName:@"whatever"];

NSError * error = nil;
[self.fetchedResultsController performFetch:&error];
if (error) {
    DDLogError(@"[%@ %@] fetchedResultsController ERROR: %@", THIS_FILE, THIS_METHOD, error.localizedDescription);
}
[self.myTableView reloadData];

The problem is that the table view does not reload. If I scroll the table view, the cells change based on new data, but the number of sections and rows does not change as it should each time I try to filter.

What am I missing?

Upvotes: 1

Views: 1201

Answers (2)

andrewbuilder
andrewbuilder

Reputation: 3791

I suspect you need to insert and delete the table view rows based on your updated data.

The error message you wrote in the comments to the other answer holds the clue.

Relevant Apple documentation.

What you should specifically read is the text under the title: Batch Insertion, Deletion, and Reloading of Rows and Sections.

Before this however, as you are changing your NSFetchedResultsController, you should check that you implement NSFetchedResultsController delegate methods to manage the changes. There is standard code available from Apple that you should consider including in your table view controller. You should not need to change / override this standard code.

See this Apple documentation.

Essentially for the "Typical Use" case you will want to implement the four typical delegate methods:

  • (void)controllerWillChangeContent:(NSFetchedResultsController *)controller ;
  • (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo ;
  • (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath ;
  • (void)controllerDidChangeContent:(NSFetchedResultsController *)controller .

In the case that you've already included these NSFetchedResultsController delegate methods, you may need to consider using the UITableView delegate methods beginUpdates and endUpdates with the methods:

- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation: (UITableViewRowAnimation)animation;
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation: (UITableViewRowAnimation)animation;

So you should try using the following code block:

tableView.beginUpdates;
tableView.insertRowsAtIndexPaths:indexPaths withRowAnimation:animation;
tableView.deleteRowsAtIndexPaths:indexPaths withRowAnimation:animation;
tableView.endUpdates

You'll obviously need to create the arrays of indexPaths for the insert and delete methods.

Upvotes: 0

Idali
Idali

Reputation: 1023

you need to delete cache BEFORE any change to fetchResultController

From apple doc : NSFetchedResultsController Class Reference

If you are using a cache, you must call deleteCacheWithName: before changing any of the fetch request, its predicate, or its sort descriptors. You must not reuse the same fetched results controller for multiple queries unless you set the cacheName to nil.

Optionally, the name of the cache file the controller should use (passing nil prevents caching). Using a cache can avoid the overhead of computing the section and index information.

Upvotes: 1

Related Questions