CloudKit - distanceToLocation:fromLocation

Is there an issue with distanceToLocation:fromLocation CloudKit predicate?


I am trying to fetch some results from cloud db using the following code:


CGFloat radius = 1000.00; // in meters
CLLocation *currentLocation =
[[CLLocation alloc] initWithLatitude:42.713834 longitude:23.264086];
   
CKLocationSortDescriptor *sortDescriptor =
[[CKLocationSortDescriptor alloc] initWithKey:@"location" relativeLocation:currentLocation];
   
[NSPredicate predicateWithFormat:@"distanceToLocation:fromLocation:(location, %@) < %f",
currentLocation, radius];

I am expecting four records but result contains seven. Three of them exceed my predicate rule.


Result:

lat:42.713402 long:23.275190 distance:910.919712

lat:42.710781 long:23.271160 distance:671.444867

lat:42.734921 long:23.247850 distance:2693.570884

lat:42.724411 long:23.281340 distance:1837.812711

lat:42.721882 long:23.270849 distance:1051.719189

lat:42.714920 long:23.263790 distance:123.055994

lat:42.715111 long:23.261801 distance:234.863830


Distance in the result above was measured for every record with:

[[record objectForKey:@"location"] distanceFromLocation:currentLocation];


Sort descriptor also does not work properly.

By documentation sort descriptor should sort results in ascending order - but it is not true

https://developer.apple.com/library/prerelease/ios/documentation/CloudKit/Reference/CKLocationSortDescriptor_class/index.html#//apple_ref/occ/instm/CKLocationSortDescriptor/initWithKey:relativeLocation:

Replies

Have you filed a bug report for it? It's probably worth doing so. The more reports, the more likely someone is to spend time on improving it.


My report for the broken sort descriptor bug is 25822198. I filed it four months ago and it's still open. If more people are filing similar reports, we might see more action on it.


For the distance limit bug, I'm inclined to believe it's an optimisation thing, that it's optimising for a result set size rather than a closeness to the requested distance limit. That meaning that if the database has reduced the possible results for your query to a small enough set (even if many in that set are outside of your given limit) it will return the results immediately rather than spending time on further refining the result set. So the database is prefering fast results and small result set size rather than strict accuracy to the requested limit.


If I'm right about that optimisation, then it means that as your database grows larger you should start to see less results that are outside of your limit. Though I haven't spent any time testing that theory yet.

I added code to further filter the results using distance(from: loc) and that works.

Note that the radius is in meters.

I got a reply back on my bug report. Here is what they said.


The granularity of location is ~10km and records within range that would be returned. And, the correct unit is *meters*.


I"ve closed my bug report and will further filter the data using the distance(from: loc) method.

Just spent hours trying to CKLocationSortDescriptor to work now finding this I am not sure if I am happy that it isn't me doing something wrong or sad that it doesn't work!!!


Do you have any idea for a workaround to order by distance from user using Cloudkit and in swift.


I am new to iOS dev so any help would be amazing!


Thanks

Can you use the Objective C code above in your project? In that code, theResults is the unsorted 'sorted' array returned by the query and theSortedResults is the sorted results that should have been returned.

Hi SpaceMan:

Can you expplain how to use the distance(from: loc) method?

I'm also running into issues with CLLocation and the distance predicate. I get this:

Cloud Query Error - Fetch Locations: <CKError 0x60000153a490: "Invalid Arguments" (12/1009); "Field 'location' has a value type of BYTES and cannot be queried using filter type NEAR">



Code Block  swift
  static func fetchNearbyLocations(_ location: CLLocation, radiusInMeters: CLLocationDistance, onComplete: @escaping (_ records:[CKRecord]?) -> Void) {
        // convert radius in meters to kilometers
        let radiusInKilometers = radiusInMeters / 1000.0
        //set up predicate for query
        let locationPredicate = NSPredicate(format: "distanceToLocation:fromLocation:(location, %@) < %@", location, NSNumber(value: radiusInKilometers))
        //set up query based on predicate
        let query = CKQuery(recordType: "Location", predicate: locationPredicate)
        //Execute query
        CKContainer(identifier: CloudKitHelper.containerId).publicCloudDatabase.perform(query, inZoneWith: nil) { (records, error) in
            if let error = error {
                DispatchQueue.main.async {
                    print("Cloud Query Error - Fetch Locations: \(error)")
                }
            } else {
                onComplete(records)
            }
        }
    }

The location attribute in the entity is type Transformable with Custom Class CLLocation. What am I missing here?

Thanks in advance.