Search code examples
iosiphonecore-locationcllocationmanagerbackground-fetch

Can i use startupdatinglocation in background?


I need to know if I am near to a saved Geo-location in my app within a range starting at 10 Meters to 500 Meters in background. I have used startMonitoringForSignificantLocationChange but I am not getting accurate results. Instead using startupdatinglocation is giving results as accurate as 7 meters. I am planning to add a NSTimer to call startupdatinglocation every 1 minute.

Is there any other solutions?


Solution

  • First of all, please don't call startUpdatingLocation every minute. You will kill your users battery life on their devices. Location services utilizes a large amount of battery power to operate. Also, once you call startUpdatingLocation, it will run until you call stopUpdatingLocation.

    There are properties you can set on CLLocationManager to configure it to your needs. Like...

    @property(assign, nonatomic) CLLocationDistance distanceFilter
    @property(assign, nonatomic) CLLocationAccuracy desiredAccuracy
    

    distanceFilter description: The minimum distance (measured in meters) a device must move horizontally before an update event is generated.

    desiredAccuracy can be one of the following:

    kCLLocationAccuracyBestForNavigation
    kCLLocationAccuracyBest;
    kCLLocationAccuracyNearestTenMeters;
    kCLLocationAccuracyHundredMeters;
    kCLLocationAccuracyKilometer;
    kCLLocationAccuracyThreeKilometers;
    

    Depending on the configuration of your CLLocationManager object, the device will use certain geo location hardware versus others. Sometimes simply using cell tower triangulation will be sufficient and is the quickest way to get a semi accurate location. Other times it might need to utilize GPS, which will drain battery life very quickly and takes much longer to achieve a location but is in most cases much more accurate.

    I would recommend starting an NSTimer, but utilize it for the opposite of what you had planned. Utilize it to stopUpdatingLocation after a reasonable amount of time if CLLocationManager isn't able to get an accurate enough location. You DO NOT want to leave location services running for ever. Having this timer set to something like 30 seconds will ensure that it is shut down after the timeout.

    Also, you will need to implement the CLLocationManagerDelegate method:

    - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
    

    In this method you will need to check the timestamp of the location to ensure it is recent. You will also need to check the horizontalAccuracy of the location to ensure it is within the desired accuracy range. Here is an example of how to do this...

    - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    
        // Ensure we get a recent and accurate location
        NSDate * locationTimestamp = [newLocation timestamp];
        NSTimeInterval timeDifference = [locationTimestamp timeIntervalSinceNow];
        double thirtyMins = (30 * 60 * -1);
        BOOL locationTimestampIsCurrent = (timeDifference > thirtyMins);
        BOOL isAccurateLocation = (newLocation.horizontalAccuracy <= 3000.00);
    
        if (locationTimestampIsCurrent && isAccurateLocation) {
            NSLog(@"Shutting down location services, we have a good locaton.");
            [self stopListeningForLocation];
        } else {
            // Do nothing, let this method be called again with a new "newLocation" to check its validity.
        }
    }
    

    From iOS 6 the above method is deprecated, instead use the following delegate method:

    - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
    

    To get the newest location simply fetch the last object of the locations array like so:

      CLLocation *location = [locations lastObject];