Location in Core Data sorted by distance via NSFetchedResultsController?

I have a set of entity objects in an iOS master data database that describe something in a location. Let me name the location of the object. I implemented this by having two attributes in the Location that relate to the location - latitude and longitude, both double. There are other elements, such as name.

I am using NSFetchedResultsController to bind objects to a UITableViewController. What I would like to do is sort the results by distance to a given CLLocationCoordinate2D. In a really perfect scenario, I can update this list to recalculate sorting based on the new location. Therefore, this view will depend on two keys and a third β€œstatic” variable (one that does not depend on the elements of the collection).

I think I can figure out how to do this if I sort an arbitrary list with NSSortDescriptors. However, I do not control how sort descriptors are used in NSFetchedResultsController.

Is there a way to configure my objects, my NSFetchedResultsController, my NSSortDescriptors, etc., to accomplish this? I suspect the answer is not to create a fancy NSSortDescriptor, but to create a temporary attribute in an entity that represents the distance to me, and recount this attribute. However, I'm new enough to Core Data, I'm not sure how to do this (iterate over all objects and recount the field). I'm also not sure if NSSortDescriptors will work with Transient attributes.

+7
source share
2 answers

(From the comments :)

A sample query for a Core Data store (based on SQLite) cannot use sorting descriptors based on transient attributes or predicates based on Objective-C.

If you do not want to lose the benefits of the resulting result controller (for example, update animated tables, automatically group by sections, etc.), you must first calculate the distance to the current location and save it in the (permanent) attribute of your objects.

Alternatively, you can get all the objects and sort them in memory. In this case, you can use arbitrary sort descriptors. But this cannot be combined with the selected result controller, so you will have to register for changes in the context of the managed object and reload the table if necessary.

+12
source

I found the BSFetchedResultsController github project , which is a subclass of NSFetchResultsController, which does what Martin suggested in that it sorts in memory using an arbitrary sort descriptor, in addition, it also registers changes in context and again calculates any index changes taking into account arbitrary sort descriptor. All in all a very impressive feat! I have successfully used it to sort by distance as follows:

BSFetchedResultsController* fetchedResultsController = [[BSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil]; // create a location to compare distance to, eg current location CLLocation* sourceLocation = [[CLLocation alloc] initWithLatitude:55.87595153937809 longitude:-4.2578177698913855]; // compare the distance from both to the source location fetchedResultsController.postFetchComparator= ^(id a, id b) { Venue* v1 = (Venue*)a; Venue* v2 = (Venue*)b; double d1 = [v1.coreLocation distanceFromLocation:sourceLocation]; double d2 = [v2.coreLocation distanceFromLocation:sourceLocation]; return [@(d1) compare:@(d2)]; }; NSError *error = nil; if (![fetchedResultsController performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); } 

Since it has an old project, it does not have ARC, so when you include two files, do not forget to mark .m with the -fno-objc-arc compiler flags in Target, Build Phases. Also keep in mind that the developer believes that the code is not ready for production, so be sure to use the appropriate tests if you use it.

In my code above, I have a temporary coreLocation property on my subclass of the managed entity object, you can see how to achieve this here . In addition, calculating distances is inefficient; you may need to cache the distance in the object, rather than recount it with each comparison.

Finally, it seems that this project arose because the creator of Daniel Thorpe Stackoverflow's question remained unanswered, forcing him to solve the problem and post the only answer himself, so I think that if you find his project useful, you could kindly vote for his post like me.

0
source

All Articles