How to filter Core Data results from NSFetchedResultsController after the fetch

I'm trying to display some Spots in a UITableView with NSFetchedResultsController associated to the Database.

The problem is that I need to filter the results by a distance (radio) from a specific Location. I read that it is not possible to a NSPredicate that calculate distance, so I'm getting all the Spots in an area around the Location with a simple comparation with the coordinates. That gives me a Square around the Location. Then, I want to iterate the results of the fetchedObjects and remove the ones that are not in the Zone.

- (NSFetchedResultsController *)newFetchedResultsControllerWithSearch:(NSString *)searchString{


NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                                            managedObjectContext:self.managedObjectContext
                                                                                              sectionNameKeyPath:sectionName
                                                                                                       cacheName:@"Stores"];
aFetchedResultsController.delegate = self;

NSError *error = nil;
if (![aFetchedResultsController performFetch:&error])
{
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}

// ITERATE FETCHED OBJECTS TO FILTER BY RADIO


int n = [aFetchedResultsController.fetchedObjects count];
NSLog(@"Square result count: %i",n);

self.footerLabel.text = [NSString stringWithFormat:NSLocalizedString(@"%i Spots", @"%i Spots"),n];

return aFetchedResultsController;

How can I remove object from the aFetchedResultsController.fetchedObjects before returning it?? Will it alterate the methods used in UITableView like [fetchedResultsController objectAtIndexPath:indexPath] ?

Thanks

Upvotes: 1

Views: 3748

Answers (5)

Tom Andersen
Tom Andersen

Reputation: 7200

Geohash.

So its a string that has the location where you can do a 'like' SQL query and get results around an area.

From wikipedia " Geohash is a geocoding system invented by Gustavo Niemeyer and placed into the public domain. It is a hierarchical spatial data structure which subdivides space into buckets of grid shape, which is one of the many applications of what is known as a Z-order curve, and generally space-filling curves.

Geohashes offer properties like arbitrary precision and the possibility of gradually removing characters from the end of the code to reduce its size (and gradually lose precision).

As a consequence of the gradual precision degradation, nearby places will often (but not always) present similar prefixes. The longer a shared prefix is, the closer the two places are."

Upvotes: 0

Roman Simenok
Roman Simenok

Reputation: 570

Use you fetchedResultController

NSError *error;

    if (filter) { // check if you need to filter
        // set predicate to self.fetchedResultController.fetchRequest
        self.fetchedResultController.fetchRequest.predicate = [NSPredicate predicateWithFormat:@"your filter params"]];
    }else{ 
        // if you dont need to filter and you need to show all objects set predicate to nil
        self.fetchedResultController.fetchRequest.predicate = nil;
    }

    // and perform fetch!
    if (![self.fetchedResultController performFetch:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    }

don't forget update UI after perFormFetch

Upvotes: 0

Martin R
Martin R

Reputation: 540055

You cannot modify the result set of a fetched results controller (FRC). You can filter the results, but that does not help if the FRC is used as data source for a table view.

Possible solutions:

  • Pre-calculate the distances to the current locations and store the distances as a persistent attribute in the database. Then you can use a filter on the distance directly in the fetch request of the FRC. The big disadvantage of course is that you have to re-calculate the distances when the current location changes.

  • Don't use a fetched results controller. Perform a fetch request and filter the results of the fetch request according to the radius into an array, and use this array as data source for the table view. The big disadvantage here is that you lose the automatic update features of the FRC. You would have to reload the table view each time the radius or other search parameters are changed.

I know that both solutions are not satisfying, but I don't think there is a method to combine a fetched results controller with a function based filter.

Upvotes: 2

Mundi
Mundi

Reputation: 80271

The proper method to do this is to set a new fetch request which includes your predicate. The fetched results controller's fetchRequest is read only, so you have to put the logic into the the lazy initialization of the fetched results controller itself.

if (searchIsActive) {
   fetchRequest.predicate = 
       [NSPredicate predicateWithFormat:
          @"(%K < %@ && %K > %@) && (%K < %@ && %K > %@)",
          @"lat", @(locationLat + limit), @"lat", @(locationLat - limit),
          @"lon", @(locationLon + limit), @"lon", @(locationLon - limit)];
}
else { fetchRequest.predicate = nil; }

Then, to update the table, just set the controller to nil and reload

self.fetchedResultsController = nil;
[self.tableView reloadData];

Upvotes: 0

Nikola Kirev
Nikola Kirev

Reputation: 3152

Use NSPredicate to search/filter trough the array.

Example:

NSString *modelName = @"honda";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"model == %@", modelName];
NSArray *filteredArray = [results filteredArrayUsingPredicate:predicate];

Upvotes: -1

Related Questions