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
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?
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.