Dowemo
0 0 0 0


Question:

I'm looking for a way to get a background location update every n minutes in my iOS application. I'm using iOS 4.3 and the solution should work for non-jailbroken iPhones.

I tried / considered following options:

  • CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges: This works in the background as expected, based on the configured properties, but it seems not possible to force it to update the location every n minutes
  • NSTimer: Does work when the app is running in the foreground but doesn't seem to be designed for background tasks
  • Local notifications: Local notifications can be scheduled every n minutes, but it's not possible to execute some code to get the current location (without the user having to launch the app via the notification). This approach also doesn't seem to be a clean approach as this is not what notifications should be used for.
  • UIApplication:beginBackgroundTaskWithExpirationHandler: As far as I understand, this should be used to finish some work in the background (also limited in time) when an app is moved to the background rather than implementing "long-running" background processes.

How can I implement these regular background location updates?


Best Answer:


On iOS 8/9/10 to make background location update every 5 minutes do the following:

  • Go to Project -> Capabilities -> Background Modes -> select Location updates

  • Go to Project -> Info -> add a key NSLocationAlwaysUsageDescription with empty value (or optionally any text)

  • To make location working when your app is in the background and send coordinates to web service or do anything with them every 5 minutes implement it like in the code below.

  • I'm not using any background tasks or timers. I've tested this code with my device with iOS 8.1 which was lying on my desk for few hours with my app running in the background. Device was locked and the code was running properly all the time.

    @interface LocationManager () <CLLocationManagerDelegate>
    @property (strong, nonatomic) CLLocationManager *locationManager;
    @property (strong, nonatomic) NSDate *lastTimestamp;
    @end
    @implementation LocationManager
    + (instancetype)sharedInstance
    {
        static id sharedInstance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sharedInstance = [[self alloc] init];
            LocationManager *instance = sharedInstance;
            instance.locationManager = [CLLocationManager new];
            instance.locationManager.delegate = instance;
            instance.locationManager.desiredAccuracy = kCLLocationAccuracyBest; // you can use kCLLocationAccuracyHundredMeters to get better battery life
            instance.locationManager.pausesLocationUpdatesAutomatically = NO; // this is important
        });
        return sharedInstance;
    }
    - (void)startUpdatingLocation
    {
        CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
        if (status == kCLAuthorizationStatusDenied)
        {
            NSLog(@"Location services are disabled in settings.");
        }
        else
        {
            // for iOS 8
            if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
            {
                [self.locationManager requestAlwaysAuthorization];
            }
            // for iOS 9
            if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
            {
                [self.locationManager setAllowsBackgroundLocationUpdates:YES];
            }
            [self.locationManager startUpdatingLocation];
        }
    }
    - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
    {
        CLLocation *mostRecentLocation = locations.lastObject;
        NSLog(@"Current location: %@ %@", @(mostRecentLocation.coordinate.latitude), @(mostRecentLocation.coordinate.longitude));
        NSDate *now = [NSDate date];
        NSTimeInterval interval = self.lastTimestamp ? [now timeIntervalSinceDate:self.lastTimestamp] : 0;
        if (!self.lastTimestamp || interval >= 5 * 60)
        {
            self.lastTimestamp = now;
            NSLog(@"Sending current location to web service.");
        }
    }
    @end



    Copyright © 2011 Dowemo All rights reserved.    Creative Commons   AboutUs