1

I am doing background location tracking in an app and it is firing way too often. I'm not sure what is the best technique for setting a minimum time interval for updating location. I only want to save location data every 2 minutes or so. Some have said to use an NSTimer but I'm not sure how or where to incorporate that in my current code. Below is all of my location tracking code in AppDelegate, does anyone know the best technique for decreasing the frequency of location updates given my current code?

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
{
    isBackgroundMode = NO;
    _deferringUpdates = NO;

    self.locationManager = [CLLocationManager new];
    [self.locationManager setDelegate:self];
    if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
        [self.locationManager requestAlwaysAuthorization];
    }
    [self.locationManager startUpdatingLocation];
    [self.locationManager startMonitoringSignificantLocationChanges];
    [self initializeRegionMonitoring];
}


-(void) initializeRegionMonitoring {
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;

    // notify changes when the device has moved x meters
    self.locationManager.distanceFilter = 10;
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    [self.locationManager startUpdatingLocation];
    [self.locationManager startMonitoringSignificantLocationChanges];
}

// Delegate method from the CLLocationManagerDelegate protocol.
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    [self saveLocation];

    //tell the centralManager that you want to deferred this updatedLocation
    if (isBackgroundMode && !_deferringUpdates)
    {
        _deferringUpdates = YES;
        [self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:60];
    }

}

- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {

    _deferringUpdates = NO;    
}


-(void) saveLocation {
    // save information database/ communication with web service
}

- (void)applicationWillResignActive:(UIApplication *)application {
    isBackgroundMode = YES;

    [self.locationManager stopUpdatingLocation];
    [self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [self.locationManager setDistanceFilter:kCLDistanceFilterNone];
    self.locationManager.pausesLocationUpdatesAutomatically = NO;
    self.locationManager.activityType = CLActivityTypeAutomotiveNavigation;
    [self.locationManager startUpdatingLocation];
}


-(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
    return true;
}

-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

    [self saveLocation];

    completionHandler(UIBackgroundFetchResultNewData);
    NSLog(@"Fetch completed");
}

Also since a lot of what I am doing is compile from various tutorials, please feel free to point out anything I am doing incorrectly in terms of performance or battery usage. Thanks!

MSU_Bulldog
  • 3,501
  • 5
  • 37
  • 73

4 Answers4

4

In other (open source) projects I have seen the following strategies to save power:

  • Reduce the requested accuracy (setDesiredAccuracy) very big (not "kCLLocationAccuracyBest", but actually "kCLLocationAccuracyKilometer")
  • Reduce the callback "didUpdateLocations:" count by setting a large threshold in "distanceFilter"
  • Return from callback if writes are not needed (with other criteria such as time)

Any way I do not see the need for an extra timer here. What you want to do is start with a rough accuracy (if you can!) and additionally set your distanceFilter to a high value (more than 10 meters for example).

What you can do additionally is this: in your "didUpdateLocations:" callback, let the "saveLocation" only be called if "lastSaveTime" is more than 5 minutes ago if(lastSaveTime + 5 < thisSaveTime) { ... } assuming time measured in seconds.

Peter Branforn
  • 1,679
  • 3
  • 17
  • 29
  • Thanks for the extra suggestions, I've changed desiredAccuracy to kCLLocationAccuracyTenMeters and increased the distanceFilter from 10 to 30. Question about lastSaveTime: I do not have that, so are you suggesting I create an NSTimer for the last saved time or some NSDate variable in NSUserDefaults for the last time it was saved? – MSU_Bulldog Sep 07 '15 at 15:06
  • Hi, you do not necessarily need a timer for that: you basically just use a time interval to measure how much time has passed between two subsequent callbacks. See this: http://stackoverflow.com/questions/4371757/how-can-i-calculate-the-difference-between-two-dates – Peter Branforn Sep 07 '15 at 15:08
  • Ok probably less taxing on memory/battery usage anyways. I'll save an NSDate to NSUserDefaults for the last saved date then return if its less than a few minutes. – MSU_Bulldog Sep 07 '15 at 15:11
  • That would be a way to go, I would even try to keep it as a local property only (to even save the writing to NSUserDefaults). But maybe I misunderstood your intention of the NsUserDefaults, maybe you just wanted to store the "minimum interval"? – Peter Branforn Sep 07 '15 at 15:13
  • No you understood correctly, I was thinking if it was a local property and the user terminated the app then it would lose its value. Thats why I was going to save it in NSUserDefaults. Does that sound like the right way to go? – MSU_Bulldog Sep 07 '15 at 15:14
  • Yes, I would say so. In your scenario (terminate -> restart) I would think that "last saved time" is not so important, so I thought you might even skip that. Hopefully the user will not restart the app every two seconds :-). – Peter Branforn Sep 07 '15 at 15:16
  • Question: didUpdateLocations is still getting called in the background every second, I solved the problem with getting data sent to the server but won't that be taxing on the phone if that is getting called so often? – MSU_Bulldog Sep 07 '15 at 15:30
  • solved it, can't edit my comment, sorry! I needed to update distanceFilter and desiredAccuracy in applicationWillResignActive. Thanks! – MSU_Bulldog Sep 07 '15 at 15:37
1

As you have declared in your

-(void) initializeRegionMonitoring

the value of distance filter to 10

self.locationManager.distanceFilter = 10;

Therefore, after user move to 10 meters, your didUpdateLocations will be called,

If you want to minimize the call of this delegate method, then you have to increase the value of distanceFilter.

Rajat
  • 10,977
  • 3
  • 38
  • 55
0
  1. Start the service when user Moves and stop the service when location. Accuracy is less than locatonManager's Accuracy.
  2. Location manager should be paused automatically & try to use resume locations delegate.

  3. Check time of last location and new location.

  4. This will reduce some battery.

SlateEntropy
  • 3,988
  • 1
  • 23
  • 31
-1
self.locationManager.distanceFilter = 10;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest; 
[self.locationManager startUpdatingLocation];

Try This....!!

P.J.Radadiya
  • 1,493
  • 1
  • 12
  • 21
  • Hi PJ, your answer appeared in the Low Quality Answers review queue. Please update your answer with a better explanation of what exactly your code does. Also, please read [answer] – Dan Beaulieu Mar 11 '16 at 22:06