Search code examples
iosswiftcore-datanspredicatecllocation

CoreData Predicate with Function Argument


I am attempting to include a function in a Predicate definition. Is this possible?

Let's say you have a Core Data entity of Places with attributes for latitude and longitude.

I would like to add annotations to a mapview of those Places within a specified distance from the user location. I can, of course, loop through the entire database and calculate the distance between each Place and the user location but I will have about 35000 places and it would seem that it would be more efficient to have a predicate in the fetchedResultsController setup.

I tried the code below but I receive an error message of "Problem with subpredicate BLOCKPREDICATE(0x2808237b0) with userInfo of (null)"

func myDistanceFilter(myLat : CLLocationDegrees, myLong : CLLocationDegrees, cdLat : CLLocationDegrees, cdLong : CLLocationDegrees) -> Bool {

    let myLocation = CLLocation(latitude: myLat, longitude: myLong)
    let cdLocation = CLLocation(latitude: cdLat, longitude: cdLong)

    if myLocation.distance(from: cdLocation) < 5000.0 {
        return true
    } else {
        return false
    }
}//myDistancePredicate

And inside the fetchedResultsController:

let distancePredicate = NSPredicate {_,_ in self.myDistanceFilter(myLat: 37.774929, myLong: -122.419418, cdLat: 38.0, cdLong: -122.0)}

If it is possible to have a block/function inside a predicate how do you get a reference to an attribute for the entity object being evaluated?

Any guidance would be appreciated.


Solution

  • Well, yes it is possible. NSPredicate does have +predicateWithBlock: and init(block:).

    However if you scroll down on that page you see the bad news:

    Core Data supports block-based predicates in the in-memory and atomic stores, but not in the SQLite-based store.

    So, whether you use an in-memory store, or do the filtering in code, either way you need to bring these 35,000 items into memory, and performance of a brute force approach will be poor.

    There is a point of complexity at which SQL is no longer appropriate – you get better performance with real code. I think your requirement is far beyond that point. This will be an interesting computer science project in optimization. You need to do some pre-computing, analagous to adding an index to your database. Consider adding a region attribute to your place entities, then write your predicate to fetch all places within the target location's region and all immediate neighbors. Then filter through those candidates in code. I'm sure this has been done by others – think of cells in a cell phone network – but Core Data is not going to give you this for free.