Dawood Mujib
Dawood Mujib

Reputation: 377

NSfetchedResultsController changes not reflecting to UI

Problem with NSFetchedResultsController is that I fetched the data and it populates the UITableView with cacheName set to nil. Later when I change the predicate of NSFetchedResultsController and called perfromFetch, it won't refreshes the UITableView however the data inside NSFetchedResultsController is updated. One thing more, NSFetchedResultsControllerDelegate methods are also not invoking.

Thanks in Advance.

Edited: Added Code

NSFetchedResultsControllerDelegate

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

    UITableView *tableView = self.tableView;

    switch(type) {

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeMove:
        case NSFetchedResultsChangeUpdate:
            break;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView endUpdates];
}

NSFetchedResultsController

- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController) {
        return _fetchedResultsController;
    }

    NSManagedObjectContext *context = [GmailDBService sharedService].managedObjectContext;
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Thread"];
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"historyId" ascending:NO];
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"SUBQUERY(messages, $m, ANY $m.labels.identifier == %@).@count > 0", self.label.identifier];
    fetchRequest.sortDescriptors = @[sortDescriptor];
    fetchRequest.fetchBatchSize = 20;

    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
    _fetchedResultsController.delegate = self;
    [_fetchedResultsController performFetch:nil];

    return _fetchedResultsController;
}

Predicate Refreshing

- (IBAction)unwindToThreadsController:(UIStoryboardSegue *)segue
{
    self.fetchedResultsController.fetchRequest.predicate = [NSPredicate predicateWithFormat:@"identifier == %@", @"15f1919682399cc9"];
    [self.fetchedResultsController performFetch:nil];
}

Upvotes: 0

Views: 268

Answers (1)

Jon Rose
Jon Rose

Reputation: 8563

fetchedResultsController are not expensive. You should not be afraid to create and destroy them as needed. When you need to change the predicate discard the current fetchedResultsController and create a new one. Don't forget to reload the tableView after you do so.

The changes aren't triggering the fetchedResultsController because you are monitoring threads, but your predicate is based on messages. So when a message is changed it does not trigger a change in the fetchedResultsController. The controller only monitors changes to one entity and does not expect changes to other entities to effect it. You can fix this in a few ways:

  1. setup your fetchedResultsController to look at message and group them by threads, then only display every section (ie thread) as a row.
  2. every time you change a message also change its thread (message.thread.threadId = message.thread.threadId will count as a change as far as the fetchedResultsController is concerned).

Also fetchBatchSize isn't respected by a fetchedResultsController so you should remove it for clarity.

Upvotes: 1

Related Questions