0

The following code is from the accepted answer of this post, regarding with implement search bar in table view with Core Data. It uses two fetched results controller (FRC). One for the "normal" table view, one for search results table view. And it uses a helper method to decide which FRC to use for table view data source methods, FRC delegate methods, etc..  

I understand most of the code except the following part. What code should I put in to replace the comments "update the filter..."? And why should I set the self.searchFetchedResultsController and its delegate to nil?

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSInteger)scope
{
    // update the filter, in this case just blow away the FRC and let lazy evaluation create another with the relevant search info
    self.searchFetchedResultsController.delegate = nil;
    self.searchFetchedResultsController = nil;
    ...
}
Community
  • 1
  • 1
Philip007
  • 3,190
  • 7
  • 46
  • 71

2 Answers2

1

Here is what happens when you set the fetchedResultsController to nil. The next time the table view (or search results table view) needs data, it will query its datasource which in turn references the (non-existing) FRC. If you look into the fetchedResultsController method you will see that the FRC is created lazily - only if it is nil. In the initialization routine, the fetch request is executed and a fresh result is made available to the data source.

While this works quite reliably, there is an argument to be made for not completely destroying the FRC. It is redundant and takes more time, CPU and battery power. The exact same effect can be achieved by simply calling

[fetchedResultsController performFetch:nil]; 

Now the data source will also have the newest data available.

EDIT

As stated in the comments you should really modify the fetchedResultsController method to create lazily. Look at the Apple templates for guidance.

if (_fetchedResultsController != nil) {
   return _fetchedResultsController;
}
// continue creating a new one
Mundi
  • 79,884
  • 17
  • 117
  • 140
  • "If you look into the fetchedResultsController method".. Could you be more specific about which method exactly? Is that the `- (NSFetchedResultsController *)newFetchedResultsControllerWithSearch:(NSString *)searchString `? – Philip007 Sep 23 '12 at 17:22
  • I still can't make sense of which part of the code that "FRC is created lazily - only if it is nil." Could you copy paste that part here? – Philip007 Sep 23 '12 at 17:26
  • "FRC creation code" in the link you posted. If there is no FRC `self.fetchedResultsController` is going to create a new one. You will find the fetch right in that method. It seems it is actually worse than I thought! This method will recreate the FRC **every time**. That is a very suboptimal pattern. – Mundi Sep 23 '12 at 18:18
  • I agree. I just write up a solution on this in another post. http://stackoverflow.com/questions/12555208/how-to-implement-search-in-a-table-view-with-frc – Philip007 Sep 23 '12 at 18:54
0

I briefly read through the code of the accepted answer. Much is just skeletal, showing what methods to implement. That said, I've never implemented the approach of "blowing away the FRC" in the filterContentForSearchText:scope: method; but I do have NSFetchedResultsController-backed table views with search bar capability in production code.

Here is an example of the filterContentForSearchText:scope from one of those:

- (void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope;
{
    /*  clear the filtered list */
    [_mutableFilteredLadders removeAllObjects];

    /*  in background queue filter our list */
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSPredicate *ladderTitlePredicate = [NSPredicate predicateWithFormat:@"title CONTAINS[cd] %@",searchText];
        NSPredicate *ladderTextPredicate = [NSPredicate predicateWithFormat:@"ANY ladderItems.text CONTAINS[cd] %@",searchText];
        NSPredicate *searchPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:ARRAY(ladderTextPredicate,ladderTitlePredicate)];

        [_mutableFilteredLadders addObjectsFromArray:[[self allLadders] filteredArrayUsingPredicate:searchPredicate]];

        /* reload table on the main queue */
        dispatch_async(dispatch_get_main_queue(), ^{
            [[[self searchDisplayController] searchResultsTableView] reloadData];
        });
    });
}
FluffulousChimp
  • 9,157
  • 3
  • 35
  • 42
  • Thanks for your implementation of the filter method. It's likely that doing filter in a background queue will enhance user experience. However, you use NSArray to store the search results (and possibly another array for all fetched objects). That's not the data structure I required in the post. – Philip007 Sep 23 '12 at 16:25