How to properly delete rows in UITableView (controlled by NSFetchedResultsController) while also utilizing UISearchDisplayController?

StackOverflow https://stackoverflow.com/questions/4102865

Question

In my iPhone app, I have a (non-grouped) UITableView which uses all the "bells and whistles," including (a) NSFetchedResultsController to keep it updated, (b) multiple sections, (c) the ability to add and delete items, and (d) the UISearchDisplayController to allow the user to search through the list, which can get to be very long.

I am running into issues when the user tries to delete items while they are in the process of searching. I am getting this error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of sections. The number of sections contained in the table view after the update (1) must be equal to the number of sections contained in the table view before the update (21), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted). with userInfo (null)

It seems that the problem arises from the fact that when the app switches to search mode, instead of the usual number of sections (which are alphabetical, A-Z), it switches to a single section ("Search Results"). So, when it tries to delete the item, it is thinking that the proper number of sections is the larger number when it is simply in a single section (Search Results).

Here's my code for managing the fetched results controller. Do you know what is the proper way to handle this type of action?

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// if (!self.searchDisplayController.isActive) {
  [self.tableView beginUpdates];
// }
}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)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;
    }
}


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

    UITableView *tableView = self.tableView;

    switch(type) {

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

        case NSFetchedResultsChangeDelete:

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

        case NSFetchedResultsChangeUpdate:
   if (!self.searchDisplayController.isActive) {
    [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
   } else {
   // I currently don't do anything if the search display controller is active, because it is throwing similar errors.
   }
   break;

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


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// if (!self.searchDisplayController.isActive) {
  [self.tableView endUpdates];
// }
}
Was it helpful?

Solution

If you have an NSFetchedResultsController, why don't you just create a predicate and set it on the controller as a filter?

Something like this:

NSPredicate *predicate =[NSPredicate predicateWithFormat:@"name contains[cd] %@", searchText];
[fetchedResultsController.fetchRequest setPredicate:predicate];

NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
        // Handle error
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        // Fail
}           

[self.tableView reloadData];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top