I'm having trouble displaying the results from Core Data in my UISearchDisplayController when I implement GCD. Without it, it works, but obviously blocks the UI.
In my SearchTableViewController I have the following two methods:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
// Tell the table data source to reload when text changes
[self filterContentForSearchText:searchString];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
// Update the filtered array based on the search text
-(void)filterContentForSearchText:(NSString*)searchText
{
// Remove all objects from the filtered search array
[self.filteredLocationsArray removeAllObjects];
NSPredicate *predicate = [CoreDataMaster predicateForLocationUsingSearchText:@"Limerick"];
CoreDataMaster *coreDataMaster = [[CoreDataMaster alloc] init];
// Filter the array using NSPredicate
self.filteredLocationsArray = [NSMutableArray arrayWithArray:
[coreDataMaster fetchResultsFromCoreDataEntity:@"City" UsingPredicate:predicate]];
}
You can probably guess that my problem is with returning the array from [coreDataMaster fetchResultsFromCoreDataEntity]. Below is the method:
- (NSArray *)fetchResultsFromCoreDataEntity:(NSString *)entity UsingPredicate:(NSPredicate *)predicate
{
NSMutableArray *fetchedResults = [[NSMutableArray alloc] init];
dispatch_queue_t coreDataQueue = dispatch_queue_create("com.coredata.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(coreDataQueue, ^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:entity inManagedObjectContext:self.managedObjectContext];
NSSortDescriptor *nameSort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:nameSort, nil];
[fetchRequest setEntity:entityDescription];
[fetchRequest setSortDescriptors:sortDescriptors];
// Check if predicate is set
if (predicate)
{
[fetchRequest setPredicate:predicate];
}
NSError *error = nil;
NSArray *fetchedManagedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (City *city in fetchedManagedObjects)
{
[fetchedResults addObject:city];
}
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSArray arrayWithArray:fetchedResults] forKey:@"results"];
[[NSNotificationCenter defaultCenter]
postNotificationName:@"fetchResultsComplete"
object:nil userInfo:userInfo];
});
return [NSArray arrayWithArray:fetchedResults];
}
So the thread hasn't finished executing by the time it returns the results to self.filteredLocationsArray. I've tried added a NSNotification which passes the NSDictionary to this method:
- (void)updateSearchResults:(NSNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
NSArray *array = [userInfo objectForKey:@"results"];
self.filteredLocationsArray = [NSMutableArray arrayWithArray:array];
[self.tableView reloadData];
}
I've also tried refreshing the searchViewController like
[self.searchDisplayController.searchResultsTableView reloadData];
but to no avail. I'd really appreciate it if someone could point me in the right direction and show me where I might be going wrong.
Thanks
Edit
I just want to clarify something in Christopher's solution for future reference.
When I call this method, I put the GCD call to the main queue in the competition block. Also note the change to reload the tableView.
Example
[coreDataMaster fetchResultsFromCoreDataEntity:(NSString *)entity usingPredicate:(NSPredicate *)predicate completionHandler:^(NSArray *cities){
dispatch_async(dispatch_get_main_queue(), ^{
self.filteredLocationsArray = cities;
[self.searchDisplayController.searchResultsTableView reloadData];
});
}];