FS.O
FS.O

Reputation: 401

Region Monitoring (Geofencing) drains battery (iOS)

I've implemented in my app the region monitoring feature of CLLocationManager, it works but it kills my battery:

-

Image

-

Is it should be like that?

My code:

monitorLocationViewController.m (please scroll to see the full code):

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
    //If "allStores"(NSMutableArray) isn't nil - calling "locationChangeHandler" to update monitoring
    if (self.allStores!=nil) {
        [self locationChangeHandler];
    }

    CLLocation *currentLocation=(CLLocation*)[locations lastObject];
    NSSet *monitoredRegionsSet=self.locationManager.monitoredRegions;
    [monitoredRegionsSet enumerateObjectsUsingBlock:^(CLCircularRegion *region, BOOL *stop) {
        if ([region containsCoordinate:currentLocation.coordinate]) {
            [self.locationManager stopMonitoringForRegion:region];
            [self locationManager:self.locationManager didEnterRegion:region];
        }
    }];
}

-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
     Store *store=[self storeForRegion:region];
    if (store.alreadySendNotification==NO) {
        UILocalNotification *notification=[[UILocalNotification alloc] init];
        notification.alertTitle=@"Arounder";
        notification.alertBody=[[self storeForRegion:region] address];
        [[UIApplication sharedApplication] scheduleLocalNotification:notification];

        store.alreadySendNotification=YES;
    }
}

    //For updating monitoring
-(void)locationChangeHandler
{
//If "allStores"(NSMutableArray) isn't nil
    if (self.allStores!=nil) {
        //Finding the 20 closest stores to he user's location and adding it to "twentyClosestStores"(NSMutableArray)
        [self sortClosestStores];
        //Stop monitoring "previousTwentyStores"(NSMutableArray) (20 closest stores before user's location  updated)
        [self stopMonitoringStores];
        //Start monitoring "twentyClosestStores"(NSMutableArray)
        [self startMonitoringClosestStores];
    }
}

//Start monitoring "twentyClosestStores"(NSMutableArray)
-(void)startMonitoringClosestStores
{
    //If monitoring isn't availible for "CLCircularRegion"
    if (![CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {
        NSLog(@"Monitoring is not available for CLCircularRegion class");
        return;
    }

    //Run on all "twentyClosestStores"(NSMutableArray)'s objects
    for (Store *currentStore in self.twentyClosestStores) {
        //Start monitoring "region"(CLCircularRegion)
        [self.locationManager startMonitoringForRegion:currentStore.circularRegion];
    }
}

//Stop monitoring "previousTwentyStores"(NSMutableArray) (20 closest stores before user's location  updated)
-(void)stopMonitoringStores
{
    //Run on all "monitoredRegions"(NSSet) of "locationManager"(CLLocationManager) objects
    for (CLCircularRegion *currentRegion in self.locationManager.monitoredRegions) {
        //Stop monitoring "region"(CLCircularRegion)
        [self.locationManager stopMonitoringForRegion:currentRegion];
    }
}

//Finding a store for region
-(Store*)storeForRegion:(CLCircularRegion*)region
{
    //Run on all "allStores"(NSMutableArray)'s objects
    for (Store *currentStore in self.allStores) {
        //If "currentStore"(Store)'s "circularRegion"'s identifier is equal to "region"(CLCircularRegion)'s identifier
        if ([currentStore.circularRegion.identifier isEqualToString:region.identifier]) {
            //Returning "currentStore"(Store)
            return currentStore;
        }
    }
    //Store not found - returning nil
    NSLog(@"No store found for this region: %f,%f",region.center.latitude,region.center.longitude);
    return nil;
}

AppDelegate.m:

-(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.monitorLocationVC=[[monitorLocationViewController alloc] init];
    self.monitorLocationVC.locationManager=self.locationManager;

    [self configureLocationManager];
    [self.locationManager startUpdatingLocation];

    return YES;
}

-(void)configureLocationManager
{
    //Initializing locationManager
    self.locationManager=[[CLLocationManager alloc] init];
    //setting "locationManager"'s(CLLocationManager) delegate to "self"
    self.locationManager.delegate=self.monitorLocationVC;
    //Setting "locationManager"'s(CLLocationManager)'s distance filter to none
    self.locationManager.distanceFilter=kCLDistanceFilterNone;
    //Setting "locationManager"'s(CLLocationManager)'s activityType to navigation
    self.locationManager.activityType=CLActivityTypeAutomotiveNavigation;
    //setting "locationManager"'s(CLLocationManager) desiredAccuracy to "best"
    self.locationManager.desiredAccuracy=kCLLocationAccuracyBestForNavigation;

    self.locationManager.pausesLocationUpdatesAutomatically=NO;

    //If OS version is 9 or above - setting "allowsBackgroundLocationUpdates" to YES
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9) {
        self.locationManager.allowsBackgroundLocationUpdates = YES;
    }
}

Thank you!

Upvotes: 2

Views: 1476

Answers (1)

MSU_Bulldog
MSU_Bulldog

Reputation: 3519

You only want to monitor regions, not update their location constantly in the background.

Try this:

self.locationManager.desiredAccuracy=kCLLocationAccuracyBest;

Do you really need the distanceFilter set to kCLDistanceFilterNone? That will cause more battery power to be used. You probably want to try to set that to around 10, 20, 50 or even 100 meters.

Also, in order to not update locations constantly, instead of:

[self.locationManager startUpdatingLocation];

Try just using:

[self.locationManager startMonitoringSignificantLocationChanges];

All of these things should contribute to less battery usage. When you set accuracy and distance filters to the highest possible setting, the battery is going to be drained.

EDIT: You are going to eat up a lot of battery whatever you do because of the purpose of your app. A solution I've done before with a problem similar to this is to create algorithm or formula with an NSTimer that fires every x minutes to update the user's location. (but only update regions if they have moved x meters).

  • stop location updates between firing of the NSTimer so that you aren't constantly updating locations.
  • when the timer fires, resume location updates, grab about 10 locations (so you get an accurate one), then shut off location updates until the next time the timer is fired

Upvotes: 2

Related Questions