I have an app that gets location updates in the background every 2 minutes and 25 meters traveled. My problem is that while the location tracking is working perfectly, it is seriously draining my battery. Battery usage over a 24 hour period was 30% compared to my next closest app at 14%. Is there some trick to decreasing the battery usage of my app while maintaining my accuracy and time period of location updates?
Could it be draining the battery because of the frequency of calls to didUpdateLocations:? Or could it be that I am communicating with a web service in the background for each location update? I'm really at a loss as to how to improve the battery usage, any help is much appreciated!
Below is my code in AppDelegate for getting location and sending updates to the server:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
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 = 25; // or set to 20 meters
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
[self.locationManager startUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDate *lastDate = [defaults objectForKey:#"lastSavedDate"];
if ([[NSDate date] timeIntervalSinceDate:lastDate] < 120) {
NSLog(#"LAST SAVE TIME LESS THAN 2 MINUTES");
return;
}
[self saveLocation];
//tell the centralManager that you want to deferred this updatedLocation
if (isBackgroundMode && !_deferringUpdates)
{
NSLog(#"THIS IS GETTING CALLED EVERY TIME");
_deferringUpdates = YES;
[self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:120];
}
}
- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {
_deferringUpdates = NO;
}
-(void) saveLocation {
// This code saves location data to a web service
}
- (void)applicationWillResignActive:(UIApplication *)application {
isBackgroundMode = YES;
[self.locationManager stopUpdatingLocation];
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
[self.locationManager setDistanceFilter:25];
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");
}
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
[self.locationManager setDistanceFilter:25];
Change these attributes to less accurate. You can save significant battery.
These configurations depends what kind of app it is. If it is some kind of calorie burn app , it might be much accurate. You are updating every 2 minutes , so its should be set on average value of approximate distance covered in time with some threshold.
self.locationManager.pausesLocationUpdatesAutomatically=YES;
//monitor locations for significant location change
[self.locationManager startMonitoringSignificantLocationChanges];
If your app does't require accurate location of the user you can try using significant location change which is more memory optimised way but at the end it depends on the situation what is more important i.e high location accuracy or the battery usage. I have done significant testing on this method and usually it updates the location every 4-5 mins or 500 meters whichever is earliest.
Related
How to get continuous location details when app is killed/Terminated ? startMonitoringSignificantLocationChanges method firing the didUpdateLocations delegate method after 500 meters. But i want location updates in kill mode for every 10 meters. Or i want to relaunch the application in background automatically and start the location updates in background.Currently i have the working code in background.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
[[LocationManager sharedLocarionManager] updateAccuracy:YES];
[[LocationManager sharedLocarionManager].locationManager requestAlwaysAuthorization];
[[LocationManager sharedLocarionManager].locationManager stopUpdatingLocation];
[[LocationManager sharedLocarionManager].locationManager startUpdatingLocation];
[[LocationManager sharedLocarionManager].locationManager stopMonitoringSignificantLocationChanges];
[[LocationManager sharedLocarionManager].locationManager startMonitoringSignificantLocationChanges];
}
return YES;
}
LocationManager.m
- (id)init {
self = [super init];
if(self != nil) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
self.locationManager.distanceFilter = 100; // meters
self.locationManager.delegate = self;
}
return self;
}
-(void)updateAccuracy:(BOOL)trackingAccuracy {
if (trackingAccuracy) {
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = 10.0 ; // meters
self.locationManager.allowsBackgroundLocationUpdates = YES;
} else {
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
self.locationManager.distanceFilter = 100; // meters
}
[ self.locationManager startUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
There is a work around. Though you cannot get continuous location when your app is killed but you can send silent push notification via server to your user to get the update about its location. You will get 30 second window when the app receives silent push notification. In that short time span, you have to perform your task.
I am making a GPS tracking application. The app should track the iPhone user location. In my case it tracking when app is in background and open . if I am killing my app from background I am not getting location update. Is this an any possible after killing the app, app should track the location in iOS(objective c).
There is a way to get the location update even when the app is killed/terminated by the user or iOS.
In iOS 8 and iOS7
use [locationManager startMonitoringSignificantLocationChanges] instead of [locationManager startUpdatingLocation] please check this http://mobileoop.com/getting-location-updates-for-ios-7-and-8-when-the-app-is-killedterminatedsuspended.
A sample project also in git pls go through that too https://github.com/voyage11/GettingLocationWhenSuspended
Add location updates to your application capabibities in settings:
Image : Setting capabilities to Location updates
Then, add Privacy policy description for location usage set to location always usage description:
Image : Location Always usage description in plist
Finally, add this piece of code in appdelegate:
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.locationManager = [[CLLocationManager alloc]init];
self.locationManager.delegate = self;
[self.locationManager requestAlwaysAuthorization];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
if ([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager startMonitoringSignificantLocationChanges];
} else {
[self.locationManager startUpdatingLocation];
}
return YES;
}
(void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
[locationManager stopUpdatingLocation];
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
locationManager.pausesLocationUpdatesAutomatically = NO;
locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[locationManager startUpdatingLocation];
}
(void)applicationDidEnterBackground:(UIApplication *)application {
[locationManager stopUpdatingLocation];
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
bgTask = UIBackgroundTaskInvalid;
}];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(startTrackingBg)
userInfo:nil
repeats:YES];
}
(void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
bgTask = UIBackgroundTaskInvalid;
NSLog(#"App terminated");
}];
}
(void)startTrackingBg {
[locationManager startUpdatingLocation];
NSLog(#"App is running in background");
}
I am programming in Objective C on Xcode and seem to be having a problem when my app goes into the background. I have a GPS tracking app and it works fine when the user has the app running but when it goes in the background the GPS doesn't follow the user location step by step. It pauses and then when the app re-opens, it automatically finds the location but I want the GPS to be running all the time so it won't "go off road"1 like I have in the picture attached.
I have location updates and background fetch turned on already in background modes.
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations: (NSArray *)locations
{
if (!self.timerPause) {
for (CLLocation *newLocation in locations) {
NSDate *eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (fabs(howRecent) < 10.0 && newLocation.horizontalAccuracy < 20)
other code
if (self.locationManager == nil) {
self.locationManager = [[CLLocationManager alloc] init];
}
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.activityType = CLActivityTypeAutomotiveNavigation;
self.locationManager.distanceFilter = 10; // meters
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
}
[self.locationManager startUpdatingLocation];
locationManager.allowsBackgroundLocationUpdates = YES;
Add this where you have set up the location manager. It should work :)
So I'm creating an iOS app, and I'm ranging for beacons in the background. It works well once my iPhone is awake and it continues to work even when the iPhone is locked...however the iPhone must still be awake. Once the iPhone goes to sleep my app ranges about 10 more times and then stops. If you wake the iPhone up it starts ranging again.
I've tried monitoring as well but no luck.
Can anyone tell me if this if even possible? I've searched everywhere and can't find an answer! Please find my beacon methods (which are in the AppDelegate) below
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
self.locationManager = [[CLLocationManager alloc] init];
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
self.locationManager.delegate = self;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
[self.locationManager startUpdatingLocation];
return YES;
}
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
if(beacons.count > 0) {
CLBeacon *nearestBeacon = beacons.firstObject;
if (nearestBeacon.proximity == CLProximityImmediate || nearestBeacon.proximity == CLProximityNear) {
NSLog("Beacon detected");
}
}
}
- (void)startRangingBeacons {
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:#"EBEFD083-70A2-47C8-9837-E7B5634DF525" identifier:#"receptionBeacon"];
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
Any help is appreciated
Thanks
Sonia
There is one way to awake your phone. If your app comes under the region of beacon then,
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
above method gets called automatically. So you need to put some local notification like "You are near to beacon zone" to awake the phone.
But you must reenter into the region.
Once your app enter in the region write following code to restart the beacon process,
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"End of tolerate time. Application should be suspended now if we do not ask more 'tolerance'");
[[UIApplication sharedApplication] endBackgroundTask:UIBackgroundTaskInvalid];
}];
if (bgTask == UIBackgroundTaskInvalid)
{
NSLog(#"This application does not support background mode");
} else {
//if application supports background mode, we'll see this log.
NSLog(#"Application will continue to run in background");
}
Hope this will work for you.
I need to keep track of the user location all the time (but not drain the battery).
I understand the only way to get updates after app is terminated is using startMonitoringSignificantLocationChanges.
From Apple's Location Awareness Programming Guide on startMonitoringSignificantLocationChanges:
If you start this service and your application is subsequently
terminated, the system automatically relaunches the application into
the background if a new event arrives. In such a case, the options
dictionary passed to the application:didFinishLaunchingWithOptions:
method of your application delegate contains the key
UIApplicationLaunchOptionsLocationKey to indicate that your
application was launched because of a location event. Upon relaunch,
you must still configure a location manager object and call this
method to continue receiving location events. When you restart
location services, the current event is delivered to your delegate
immediately. In addition, the location property of your location
manager object is populated with the most recent location object even
before you start location services.
I would be glad if someone could demonstrate in the code (give an example) which methods i should use
In the following code i'm tring to :
- start the location manager at appdelegate which strats the signinficant monitor changes update and startupdating.
- in didUpdateToLocation i'm calling stopupdating
- in didFinishLaunchingWithOptions when i check if i got a UIApplicationLaunchOptionsLocationKey in order to know if i'm in the background and launched due to siginificant monitor location update.
- if so, i call startMonitoringSignificantLocationChanges again (not sure why...)
and begin a UIBackgeoundTaskIdentifier for calling startupdating method.
LocationController.m :
+ (LocationController*)sharedInstance {
#synchronized(self) {
if (sharedCLDelegate == nil) {
[[self alloc] init];
}
}
return sharedCLDelegate;
}
- (id)init
{
self = [super init];
if (self != nil) {
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
[self.locationManager startUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
return self;
}
- (void) startMonitoringSignificantLocationChanges
{
[self.locationManager startMonitoringSignificantLocationChanges];
}
- (void) stopMonitoringSignificantLocationChanges
{
[self.locationManager stopMonitoringSignificantLocationChanges];
}
-(void) start{
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation{
if ( abs([newLocation.timestamp timeIntervalSinceDate: [NSDate date]]) < 30) {
self.lastLocation = newLocation;
[self updateLocation]; //sending location to server
[self.locationManager stopUpdatingLocation];
}
}
- (void)locationManager:(CLLocationManager*)manager
didFailWithError:(NSError*)error{
[self.locationManager stopUpdatingLocation];
}
AppDelegate.h :
#interface AppDelegate : NSObject <UIApplicationDelegate> {
UIBackgroundTaskIdentifier bgTask;
}
AppDelegate.m :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
id locationValue = [launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey];
if (locationValue) {
[[LocationController sharedInstance] startMonitoringSignificantLocationChanges];
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
[[LocationController sharedInstance] start]; //startupdating
return YES;
}
else {
[[LocationController sharedInstance] init];
}
}
-(void) applicationDidEnterBackground:(UIApplication *) application
{
NSLog(#"entered background Mode");
}
-(void) applicationDidBecomeActive:(UIApplication *) application
{
NSLog(#"application Did Become Active");
}
Thank you.
Using your classes, this is what I would do.
In your AppDelegate.m, when your app is in the foreground or background, I'd move the CLLocationManager to run in the foreground / background to match. The reason I'm doing this is because if the CLLocationManager is not moved to the background when the app is in the background, no location updates are sent to the CLLocationManager's callback
- (void)applicationDidEnterBackground:(UIApplication *) application {
[[LocationController sharedInstance] stop];
[[LocationController sharedInstance] startMonitoringSignificantLocationChanges];
NSLog(#"entered background Mode");
}
- (void)applicationDidBecomeActive:(UIApplication *) application {
[[LocationController sharedInstance] stopMonitoringSignificantLocationChanges];
[[LocationController sharedInstance] start];
NSLog(#"application Did Become Active");
}
So lets say your app then moves to the background, and after awhile, iOS decides it's using too much memory and kills your app.
A few minutes later, iOS then receives a location update, and respawns your app, letting it know it respawned due to a location service. You then need to re-start the background location service, as it will be the only chance your app has to do so.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
[[LocationController sharedInstance] startMonitoringSignificantLocationChanges];
}
return YES;
}
Oh, and one last change, I'm not sure why in your locationManager:didUpdateToLocation:fromLocation: method you're stopping the location service, as when you do that no more updates come through. Just leave it running, then every time a location change comes through you can send that to the server.
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
if ( abs([newLocation.timestamp timeIntervalSinceDate: [NSDate date]]) < 30) {
self.lastLocation = newLocation;
[self updateLocation]; //sending location to server
}