Search code examples
ios6core-location

didUpdateToLocations: never ending?


Based on another SO question, I've implemented the didUpdateLocations: method as follows:

- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    // Get the latest location found
    CLLocation *location = [locations lastObject];

    NSTimeInterval locationAge = -[location.timestamp timeIntervalSinceNow];

    if (locationAge > 5.0) return;
    if (location.horizontalAccuracy < 0) return;

    if (_bestEffort == nil || _bestEffort.horizontalAccuracy >= location.horizontalAccuracy) {
        _bestEffort = location;

        // Let the delegate know
        if (self.delegate && [self.delegate respondsToSelector:@selector(location:didUpdateLocationTo:)]) {
            [self.delegate location:self didUpdateLocationTo:location];
        }

        if (location.horizontalAccuracy <= _locationManager.desiredAccuracy) {
            // Great, we've found our final location so stop searching!
            [self stopTrackingLocation];

            // Let our delegate know we've finished our work
            if (self.delegate && [self.delegate respondsToSelector:@selector(location:didFinishTrackingLocationWithFinalLocation:)]) {
                [self.delegate location:self didFinishTrackingLocationWithFinalLocation:location];
            }
        }
    }
}

Everything makes sense with this code, except that I can't seem to make it work correctly. When I step through the code, I find that

  • locationAge is less than 5, which is good
  • locations horizontalAccuracy is less than 0 which is good
  • bestEfforts horizontal accuracy is the same as the locations horizontal accuracy, so it never enters that condition
  • Even if it did, it would never call the stopTrackingLocation method, as the locations horizontalAccuracy can never be < 0 (second if statement) however, my location managers desired accuracy is -2 (best for navigation), so it never actually leaves this method.

What am I doing wrong, or how should I change this to work correctly?


Solution

  • The problem is that you are trying to get a location that has a horizontalAccuracy less than your desiredAccuracy.

    if (location.horizontalAccuracy <= _locationManager.desiredAccuracy) {
    

    However, the constants kCLLoationAccuracyBestForNavigation and kCLLocationAccuracyBest are not real values, they are -2 and -1 respectively.

    The question is what accuracy do you really need for your purposes? That is the value you should check for. For test purposes, use 100 meters. This will mean that you probably have a location using GPS, not just WiFi or Cellular.

    if (location.horizontalAccuracy <= 100) {
    

    The actual horizontalAccuracy will never be less that 5 meters with an iPhone 5. It can be 10 or 20 or 50 depending on how many satellites are visible and their geometry. It can also be -1 if the system cannot get a fix at all. That is what this line is for, to ignore those invalid locations:

        if (location.horizontalAccuracy < 0) return;
    

    EDIT:
    The constants kCLLoationAccuracyBestForNavigation and kCLLocationAccuracyBest should only be used when you configure the desiredAccuracy property on the CLLocationManager object. That desiredAccuracy setting configures the Core Location hardware to enable GPS, GLONASS, etc, to try to meet your desired accuracy (it doesn't guarantee it will be met).

    The desiredAccuracy is not a value you should be using in your delegate method, it is only there to configure the CLLocationManager.