iOS locationManager: location update in my own interval with application in the background
If we run the LocationManager in the background, running all the time without a break. This method is especially demanding on the battery, and do not always need to detect location continuously. Often we need to know the location of the specified interval.
How to run an iOS application in the background?
iOS has several options how to correctly run the application in the background. These methods are enabled with iOS. We’ll use the one that your application will use and which we need. All services we can see in UIBackgroundModes array:
- audio – The app plays audible content in the background.
- location – The app provides location-based information to the user and requires the use of the standard location services (as opposed to the significant change location service) to implement this feature.
- voip – The app provides Voice-over-IP services. Apps with this key are automatically launched after system boot so that the app can reestablish VoIP services. Apps with this key are also allowed to play background audio.
- fetch – The app requires new content from the network on a regular basis. When it is convenient to do so, the system launches or resumes the app in the background and gives it a small amount of time to download any new content.
This value is supported in iOS 7.0 and later. - remote-notification – The app uses remote notifications as a signal that there is new content available for download. When a remote notification arrives, the system launches or resumes the app in the background and gives it a small amount of time to download the new content.
This value is supported in iOS 7.0 and later. - newsstand-content – The app processes content that was recently downloaded in the background using the Newsstand Kit framework, so that the content is ready when the user wants it.
- external-accessory – The app communicates with an accessory that delivers data at regular intervals.
- bluetooth-central – The app uses the CoreBluetooth framework to communicate with a Bluetooth accessory while in the background.
Service you want to use must allow the application. This we do in Info.plist and add value with key “Required backgdound modes“. We do it as follows:
How to run the standard locationManager in the background?
If we have included location service in background mode (set in info.plist), we can try run this service after switch application into background.
We will use the methods in the AppDelegate, which allows to recognize that the application was switched to background:
- applicationDidEnterBackground: – Tells the delegate that the app is now in the background.
- applicationWillEnterForeground: – Tells the delegate that the app is about to enter the foreground.
We can write a simple example that will run locationManager only if the application is in the background:
AppDelegate.h
1 2 3 4 5 6 |
@interface AppDelegate : UIResponder <CLLocationManagerDelegate>{ CLLocationManager *locationManager; UIApplication *app; } //other properties |
AppDelegate.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
//implementation AppDelegate @implementation AppDelegate //run when app is almost ready to run. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //create new CLLocationManager locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; //create sharedApplication variable app = [UIApplication sharedApplication]; return YES; } - (void)applicationWillResignActive:(UIApplication *)application{} //starts when the application switches to the background - (void)applicationDidEnterBackground:(UIApplication *)application { [locationManager startUpdatingLocation]; } //starts automatically with locationManager -(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{ NSLog(@"Location: %f, %f", newLocation.coordinates.longtitude, newLocation.coordinates.latitude); } //starts when application switches back from background - (void)applicationWillEnterForeground:(UIApplication *)application { [locationManager stopUpdatingLocation]; } - (void)applicationWillTerminate:(UIApplication *)application{} @end |
This example works so that if you switch the application into the background, starts location updating and when it switches back to the forefront, application stops updating location.
Tracking user location in intervals
In iOS natively we can’t change the interval at which the system updates the user location. IOS updates position regularly every second, if have GPS signal.
If the application is in the foreground, we could simply stop monitoring and again start it after interval, for example using NSTimer. In this case, we have to think about the life of the application. The application runs in the background and during idle stops working.
My final procedure is use NSTimer in the background by using UIApplication:beginBackgroundTaskWithExpirationHandler:. This timer triggers periodically while the application runs in the background. You can view the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
#import "AppDelegate.h" @implementation AppDelegate BOOL locationStarted = FALSE; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //set default value after application starts locationStarted = FALSE; //create CLLocationManager variable locationManager = [[CLLocationManager alloc] init]; //set delegate locationManager.delegate = self; app = [UIApplication sharedApplication]; return YES; } //update location -(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{ NSLog(@"Location: %f, %f", newLocation.coordinates.longtitude, newLocation.coordinates.latitude); } //run background task -(voud)runBackgroundTask: (int) time{ //check if application is in background mode if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { //create UIBackgroundTaskIdentifier and create tackground task, which starts after time __block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(startTrackingBg) userInfo:nil repeats:NO]; [[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] run]; }); } } //starts when application switchs into backghround - (void)applicationDidEnterBackground:(UIApplication *)application { //check if application status is in background if ( [UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { NSLog(@"start backgroun tracking from appdelegate"); //start updating location with location manager [locationManager startUpdatingLocation]; } //change locationManager status after time [self runBackgroundTask:20]; } //starts with background task -(void)startTrackingBg{ //write background time remaining NSLog(@"backgroundTimeRemaining: %.0f", [[UIApplication sharedApplication] backgroundTimeRemaining]); //set default time int time = 60; //if locationManager is ON if (locationStarted == TRUE ) { //stop update location [locationManager stopUpdatingLocation]; locationStarted = FALSE; }else{ //start updating location [locationManager startUpdatingLocation]; locationStarted = TRUE; //ime how long the application will update your location time = 5; } [self runBackgroundTask:time]; } //application switchs back from background - (void)applicationWillEnterForeground:(UIApplication *)application { locationStarted = FALSE; //stop updating [locationManager stopUpdatingLocation]; } /*** other methods ***/ - (void)applicationWillResignActive:(UIApplication *)application{} - (void)applicationDidBecomeActive:(UIApplication *)application{} - (void)applicationWillTerminate:(UIApplication *)application{} @end |
A simpler solution might be to run the timer loop:
1 2 3 4 5 6 7 8 9 10 |
__block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(startTrackingBg) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] run]; }); |
Thank you for sharing this article if helps you.
startUpdatingLocation method is not called in background and may be Timers are also disabled in background in iOS 7.
Yes, in iOS7 this code is not working. It runs upto 3 min and then app is suspended.
Timer loop solution works fine in iOS 8 Simulator. I need to test the same code in actual iOS phone. I will test this code and update the status. How ever you have done a good job. Thank you
Well, for me it keeps running forever, and I’m not understanding why. In the Apple docs they say that we have 3 minutes while the app in the background and then it’s suspended, but with that code it keeps running (That’s what I want) .
if ([[udefaults objectForKey:@”phone”] isEqualToString:@”Selected”])
{
// [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
//
// [timerForPhone invalidate];
// timerForPhone=[NSTimer scheduledTimerWithTimeInterval:[string intValue]
// target:self
// selector:@selector(methodForMakingCall)
// userInfo:nil
// repeats:YES];
// [[NSRunLoop currentRunLoop] addTimer:timerForPhone forMode:NSRunLoopCommonModes];
app = [UIApplication sharedApplication];
//create new uiBackgroundTask
bgTask= [app beginBackgroundTaskWithExpirationHandler:^{
// [app endBackgroundTask:bgTask];
// bgTask = UIBackgroundTaskInvalid;
}];
// and create new timer with async call:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// run function methodRunAfterBackground
timerForSMS = [NSTimer scheduledTimerWithTimeInterval:[string integerValue] target:self selector:@selector(methodForMakingCall) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timerForPhone forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
This is my code I have tried all possible ways but in background up 3 min it is wrking perfectly but after that my facebook is getting logout..Please help me on it I am beginner to IOS
hi ,i want to run particular method for every particular time interval when i use below mwthod
__block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(mailsending) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
it will work up to 3 minutes only ,after 3 minutes app will terminate.
could you suggest how can achieve this task..
i want to repaeat this method in app background for every certain periodic interval (every 5 minutes or 10 or 20 minutes until user enter into foreground state)
-(void)mailsending
{
[PFCloud callFunctionInBackground:@”sendEmail”
withParameters:@{@”toUser”:[arrayForTable valueForKey:@”email”],
@”fromUser”:[BabyOnBoardUser currentUser].userEmail,
@”subject”:@”BabyOnBoard”,
@”msgBody” : @”hi “}
block:^(NSString *emailStr, NSError *error) {
if (!error) {
NSLog(@”mail send successfully”);
}
}];
}
Timer doesn’t run forever! :/
Am making an ALARM app in which I am using timer in the background bit the thing is that alarm rings only if it’s 15 or 20 mins later but for hours it doesn’t.
If we set up alarm at night to wake up in the morning then it doesn’t ring :/
Please HELP !!
Hi Keshav,
Do not use the Timer in your case.
The timer is invalidated as soon as the app goes in background,
You should use Local Notifications for the Alarm App.
Hope that helps.
Navdeesh Ahuja
can i set timer when suspended mode applicaiton in ios? For didupdateLocation delegate.