Search code examples
iosiphonecore-datansfetchedresultscontrolleruisearchdisplaycontroller

Slow UISearchDisplayController with Core Data


I have a TableViewController displaying like 40 000 rows from Core Data with NSFetchedResultsController.

I implemented a live search with a UISearchDisplayController (support for IOS 7). It's working but typing on the keyboard when searching is very slow...

I'd really appreciate if someone could point me to the right direction and show me where I might be going wrong.

Here is the UISearchResultsUpdating part in my TableViewController

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
    ItemSearchScope scopeKey = controller.searchBar.selectedScopeButtonIndex;
    [self searchForText:searchString scope:scopeKey];
    return YES;
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
    NSString *searchString = controller.searchBar.text;
    [self searchForText:searchString scope:searchOption];
    return YES;
}

- (void)searchForText:(NSString *)searchText scope:(ItemSearchScope)scopeOption
{
    if (self.managedObjectContext)
{
    NSString *predicateFormat = @"%K CONTAINS[cd] %@";

    NSString *searchAttribute1 = @"attribute1";
    NSString *searchAttribute2 = @"attribute2";
    NSString *searchAttribute3 = @"attribute3";

    if (scopeOption == searchScopeDebut) {
        predicateFormat = @"%K BEGINSWITH[cd] %@";
    }

    if (scopeOption == searchScopeFin) {
        predicateFormat = @"%K ENDSWITH[cd] %@";
    }

    NSPredicate *p1 = [NSPredicate predicateWithFormat:predicateFormat, searchAttribute1, searchText];
    NSPredicate *p2 = [NSPredicate predicateWithFormat:predicateFormat, searchAttribute2, searchText];
    NSPredicate *p3 = [NSPredicate predicateWithFormat:predicateFormat, searchAttribute3, searchText];

    NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:@[p1, p2, p3]];

    [self.searchFetchRequest setPredicate:predicate];

    NSError *error = nil;
    self.filteredList = [self.managedObjectContext executeFetchRequest:self.searchFetchRequest error:&error];
    if (error)
    {
        NSLog(@"searchFetchRequest failed: %@",[error localizedDescription]);
    }
}

}


Solution

  • I ended up using a NSTimer for delaying the shouldReloadTableForSearchString method. searchTimerPopped selector is triggered only if the user stop typing the keyboard for 2 seconds.

    @property (nonatomic, retain) NSTimer *searchTimer;
    
    - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
    {
        if (self.searchTimer) {
            [self.searchTimer invalidate];
            self.searchTimer = nil;
        }
        self.searchTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(searchTimerPopped:) userInfo:searchString repeats:FALSE];
        return NO;
    }
    
    - (void)searchTimerPopped:(NSTimer *)timer {
        NSString *searchString = [timer userInfo];
        ItemSearchScope scopeKey = self.searchDisplayController.searchBar.selectedScopeButtonIndex;
        [self searchForText:searchString scope:scopeKey];
        [self.searchDisplayController.searchResultsTableView reloadData];
    }