I am trying to allow my app to run in the background for more that 10 minutes, according to this and my good below. (I want to use long background running to keep track of a location, my code here simply just use a counter for testing purposes)
Anyone can help point out what the problem is?
Implementing Long-Running Background Tasks
For tasks that require more execution time to implement, you must
request specific permissions to run them in the background without
their being suspended. In iOS, only specific app types are allowed to
run in the background:
Apps that play audible content to the user while in the background,
such as a music player app
Apps that keep users informed of their location at all times, such as
a navigation app
Apps that support Voice over Internet Protocol (VoIP)
Newsstand apps that need to download and process new content Apps that
receive regular updates from external accessories
Apps that implement these services must declare the services they
support and use system frameworks to implement the relevant aspects of
those services. Declaring the services lets the system know which
services you use, but in some cases it is the system frameworks that
actually prevent your application from being suspended.
- (void)viewDidLoad {
[super viewDidLoad];
counterTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{
}];
count=0;
theTimer=[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(countUp)
userInfo:nil
repeats:YES];
}
- (void)countUp {
{
count++;
NSString *currentCount;
currentCount=[[NSString alloc] initWithFormat:#"%ld",count];
theCount.text=currentCount;
[currentCount release];
}
}
Another Question: Can I have an iOS App run in the background forever?
----Edited code to add location, still doesnt run for more than 10 mins, any help to what i'm doing wrong?
- (void)viewDidLoad {
[super viewDidLoad];
count=0;
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
counterTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{
// If you're worried about exceeding 10 minutes, handle it here
[locationManager startUpdatingLocation];
}];
theTimer=[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(countUp)
userInfo:nil
repeats:YES];
}
(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"OldLocation %f %f", oldLocation.coordinate.latitude, oldLocation.coordinate.longitude); NSLog(#"NewLocation %f %f", newLocation.coordinate.latitude, newLocation.coordinate.longitude);
count++; NSString *currentCount;
currentCount=[[NSString alloc] initWithFormat:#"%ld",count];
theCount.text=currentCount; [currentCount release];
}
(void)countUp { [locationManager startUpdatingLocation];
{ count++; NSString *currentCount;
currentCount=[[NSString alloc] initWithFormat:#"%ld",count];
theCount.text=currentCount;
[currentCount release]; } }
So your app uses location services. Then please read the Location Awareness Programming Guide.
You need to make some changes to your Info.plist:
If your app relies on location services to function properly, add location-services to UIRequiredDeviceCapabilities
if your app requires GPS hardware, add gps to UIRequiredDeviceCapabilities
if you need to run your app longer then 10 minutes in the background, add location to UIBackgroundModes. Then your location manager will deliver locations beyond the 10-minute-limit.
you should also set NSLocationUsageDescription (can also be localized)
Getting Location Events in the Background
If your app needs location updates delivered whether the app is in the
foreground or background, there are multiple options for doing so. The
preferred option is to use the significant location change service to
wake your app at appropriate times to handle new events. However, if
your app needs to use the standard location service, you can declare
your app as needing background location services.
An app should request background location services only if the absence
of those services would impair its ability to operate. In addition,
any app that requests background location services should use those
services to provide a tangible benefit to the user. For example, a
turn-by-turn navigation app would be a likely candidate for background
location services because of its need to track the user’s position and
report when it is time to make the next turn.
See phix23's answer (and the documentation) for the details, but here I want to explain what you can expect to happen.
This is all pretty much covered in the documentation that you quoted from.
Any application can run in the background for up to ten minutes. That's what the beginBackgroundTaskWithExpirationHandler: method does. No matter which flags and options you set, that's all you'll get using that method.
For apps that need to keep track of location you can use CLLocationManager. This does not allow your app to run in the background as long as you like. Instead it notifies you when something interesting happens -- that's what the delegate is for. So you can't guarantee that your countUp method gets called every ten minutes, but you can have the OS call it when the user moves their handset a certain distance.
By adding the following in the applicationDidEnterBackground method, it seems that it can execute forever:
[app beginBackgroundTaskWithExpirationHandler:^{}];
Then you invalidate the long task in the willEnterForeground.
I succeed in iOS 6 recently, but I'm not sure it would be approved for the store.
By the way, in your plist you can alos set Required background modes and then in Item0 App registers for location updates
Related
here's yet another question on having GPS run in the background. Apparently, there are specific conditions where my app can run (seemingly indefinitely) in the background, and sometimes it will terminate after 3 minutes. My iPad is currently running on 9.3.2.
TL;DR: Did the necessary code and project configurations, but does not always run in background for longer than 3 minutes. Why?
My post will be lengthy. I have tried to keep it concise.
My app will need to send the GPS locations at every interval: 60 seconds if the user is "Logged in," and 900 seconds (15 minutes) if the user is "Logged out." I need these requirements; The program requirements are not decided by me. This app is not published to the app store either.
I understand that I need to add this in my plist:
<key>NSLocationAlwaysUsageDescription</key>
<string>Location information from this device is required for tracking purposes.</string>
Under the project capabilities, I have Background Modes -> Location updates selected, and also in the plist:
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
In my AppDelegate, I also have these 2 (located inside application:didFinishLaunchingWithOptions:):
if ([locationManager respondsToSelector:#selector(requestAlwaysAuthorization)])
{
[locationManager requestAlwaysAuthorization];
NSLog(#"===>locationManager responds to requestAlwaysAuthorization<===");
}
else
{
NSLog(#"===>locationManager not responding to requestAlwaysAuthorization! :(<===");
}
if([locationManager respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)])
{
[locationManager setAllowsBackgroundLocationUpdates:YES];
NSLog(#"===>locationManager responds to setAllowsBackgroundLocationUpdates<===");
}
else
{
NSLog(#"===>locationManager not responding to setAllowsBackgroundLocationUpdates! :(<===");
}
In my applicationDidEnterBackground, I have the beginBackgroundTaskWithExpirationHandler function as follows:
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"ending background task. Background time remaining: %f", [[UIApplication sharedApplication] backgroundTimeRemaining]);
//Do I need to uncomment the 2 lines below?
//[[UIApplication sharedApplication] endBackgroundTask:bgTask];
//bgTask = UIBackgroundTaskInvalid;
}];
So now, my biggest question is: What have I still not done yet/done wrong, that doesn't allow for background execution?
My app has a csv log file (locally) that records what GPS coordinates are being sent over to a tracking web service. In my logs, I also record events like "App to Background" and "App to Foreground" so when I retrieve the logs, I will know if it terminated after 3 minutes. Also, I log down the [[UIApplication sharedApplication] backgroundTimeRemaining] remaining time.
I have CLLocationManager *locationManager;. When my app goes into the background, I make [locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers]; and then [locationManager startUpdatingLocation]; just to keep the app running in the background. A timer changes [locationManager setDesiredAccuracy:kCLLocationAccuracyBest]; when at the interval as stated above, and change it back to [locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers] after that. All this is when the app is in the background, [locationManager stopUpdatingLocation]; only if the app is in the foreground. Do I actually need to keep the locationManager running so the app remains active in the background?
The strange part is as follows: I realised that when I send my app to the background by: (a.) Home button (b.) power button (c.) closing the iPad case, (a.) will always have the app running in the background, while (b.) and (c.) may allow the app running for more than 3 minutes ??!? Is there really a difference? Because I know the delegate functions applicationWillResignActive and applicationDidEnterBackground will be called nonetheless. Whether or not it runs more than 3 minutes seems non-deterministic.
Did I put beginBackgroundTaskWithExpirationHandler in the correct place?
Did I put [locationManager setAllowsBackgroundLocationUpdates:YES]; in the correct place (currently in the AppDelegaate)?
Running the app with xcode debugger will always work perfect, but running it without (i.e. actual use conditions) might not let the app run. With the debugger running, I NSLog the background time remaining. It might show something like 179.348015 seconds but after 3 minutes the app will continue to run. Is this issue faced by any others?
Help is very much appreciated. o/
Alright, so I have asked this 1 year ago and have not received any responses...
When I first asked this question, my intention was to turn off the GPS once the app has obtained the coordinates. So in pseudo code, the logic should go something like this:
applicationStart()
{
startTimer();
}
startTimer()
{
timer repeats: TRUE;
timer cycle: 60 seconds;
function to call: getGPS();
}
getGPS()
{
GPS start;
retrieve coordinates;
send coordinates to server;
GPS stop;
}
By doing this, I intended to save battery power, as GPS is a battery draining feature. However, by doing this, there is no guarantee that the GPS would constantly run in the background. On top of that, as I have mentioned, the results of running such logic is non-deterministic; sometimes it would run, sometimes it would not.
In the end, my current code goes as such (in pseudo code):
applicationStart()
{
GPS start;
GPS accuracy 3 kilometers;
startTimer();
}
startTimer()
{
timer repeats: TRUE;
timer cycle: 60 seconds;
function to call: getGPS();
}
getGPS()
{
GPS accuracy best;
retrieve coordinates;
send coordinates to server;
GPS accuracy 3 kilometers;
}
Notice that I do not actually turn off the GPS; in lieu of turning it off, I make it run constantly, but changed to [locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers] when I do not need it.
Of course, I am still open to a better solution. At the point of asking this question, I was running iOS9. My above solution works on iOS10 right now, and with iOS11 round the corner, I'm not sure what other changes Apple will be bringing to the API relating to GPS.
Battery consumption? Well, so far so good; My users are not complaining.
I'm starting on IOS developing, and I'm very unfamiliar with the language.
I'm trying to develop an app that will track the location of a device on the background. I've followed some tutorials and put up a code that Logs the location update.
-(void)CurrentLocationIdentifier
{
locationManager = [CLLocationManager new];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(#"%#", newLocation);
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self CurrentLocationIdentifier];
}
It works. Since I've configured the .plist file it is registered for running on background, it logs the location endlessly.
But I need the update to be periodically, with a defined interval, otherwise it would be a battery killer. I realize that there are tons of questions here on SO about Location Update on the background, but my attempts are not being successful and I'm stuck.
I've tried stoping the Update and scheduling a start with NSTimer within the location update listener:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(#"%#", newLocation);
[locationManager stopUpdatingLocation];
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:10
target:self
selector:#selector(CurrentLocationIdentifier)
userInfo:nil
repeats:NO];
}
Where CurrentLocationIdentifier is a method that requests the start update again.
But it runs the first time, and then don't fire the scheduled task! What am I doing wrong? Should it be done some other way?
If you don't need detailed location updates in the background then the significant location update service is probably the best approach.
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
... // Any other initialisation you already have
locationManager = [CLLocationManager new];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if ([CLLocationManager significantLocationChangeMonitoringAvailable] {
[locationManager stopUpdatingLocation];
[locationManager startMonitoringSignificantLocationChanges];
}
else {
//Decide what you want do to if it isn't available
}
}
- (void)applicationDidEnterForeground:(UIApplication *)application
{
[locationManager startUpdatingLocation];
[locationManager stopMonitoringSignificantLocationChanges];
}
By the way, the delegate method you are using, locationManager:didUpdateToLocation:fromLocation: is deprecated and you should use locationManager:didUpdateLocations:
UPDATE
Significant location change isn't available on all devices, so I added a check to my code. You need to decide what approach to take on a device where it isn't available - stick with standard updates, perhaps increasing distanceFilter
I have a project on Github, https://github.com/dsdavids/TTLocationHandler, that you are welcome to download and fiddle with. TTLocationHandler is a drop in class that you can set as you wish to get the sort of results others have been pointing to. I made it when I was in exactly your situation, trying to figure out how to use location services in the background.
I would strongly advise that you forget any ideas of trying to fire regularly by any scheduled timer. The idea is to set the handler to fire for events as makes sense then analyze the return and further filter down your response to only as much activity as needed. I find significant location changes to be the best trade off.
You are able to set this for tracking non stop, then respond only as often as you require. You better have a good reason for that battery hit or your app will be rejected. My own app was rejected at first, even though I was setting it to only go full bore if it was plugged in to power. I suppose they were right though. I was able to get everything I need with significant location changes and region monitoring. My app never lets the locationManager go wild and has little impact on battery.
There is no way to wake up the application after a given time interval to execute your code. You really have two options, background fetches, and either region monitoring or significant location changes.
In terms of getting location updates periodically (based on time rather than distance moved), the best you can do is utilize iOS multitasking API performFetchWithCompletionHandler, which in a nutshell allows the OS to wake you up when it determines you can have time to process. You'll have about 30 seconds of wall time to handle what you want to and go back to sleep.
https://developer.apple.com/library/ios/documentation/uikit/reference/uiapplicationdelegate_protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:performFetchWithCompletionHandler:
There are a number of steps involved to get this set up, but it's pretty easy and should point you in the right direction. Note that this will not allow you to schedule these events whenever you want, you are really limited by the OS and the algorithm it uses to determine how often you should be allowed to perform background fetches. If you take a long time and use lots of battery, you will get less frequent fetches, and if you take too much time and never complete you'll be terminated. Being "responsible" in the background ends up with the OS giving you more time.
In my experience, being as "responsible" as possible while doing location updates and sending server requests, background fetches occur approx. 15-30 minutes apart.
If you want to be woken up when users move some specified distance, region monitoring is a pretty good (although imperfect) way to handle that, and can allow your app to wake up, start a long running background task (30 seconds) and do what you need to do before ending the task and going back to sleep.
I am looking into using deferred location updates for an iOS activity tracker, which allows location services in background. I've implemented the suggested code snippets (see below). In Xcode debugging, deferred locations attempt to start a few times until location data comes in at about 1 per second. After that, it claims to succeed in starting deferrals, and the callback for the finish trigger also succeeds after the specified time period expires. However during the time, the location handler still runs once per second. I've read that this is because the phone hasn't deemed itself ready to enter the background, and that testing in Xcode does this. Note, AppDelegate's "didEnterBackground" eventhandler got called immediately when turning off the screen, and resumed when reopening app.
I ran the same code with the phone disconnected as another test, near the window with GPS, screen off, or switching to entirely different apps, and it still never actually defers the updates. I can tell because the networked update still comes in once every 30 seconds, instead of the interval of 120 seconds which is desired in the code sample below.
What else is needed to actually get deferrals to work, since there is no error occurring in starting them and they do get their finish callback? Why do location updates continue at 1 per second even when the app goes to background?
Iphone 5s, IOS 7.1.1
// .h file (partial)
#interface MotionTracker : NSObject<CLLocationManagerDelegate, UIAccelerometerDelegate>
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
// .m file (parial)
- (id) init {
if(self = [super init]){
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.distanceFilter = kCLDistanceFilterNone;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// if set to YES (default), app stops logging location at some point and doesn't resume in any timely fashion, all data points lost in between
_locationManager.pausesLocationUpdatesAutomatically = NO;
_locationManager.activityType = CLActivityTypeFitness;
}
return self;
}
// called early in program after login confirmed
- (void) startCollectingLocation {
[_locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
// logs to file when device is not in debug
// always returns 1
NSLog(#"Location update count: %d",[locations count]);
// some code here to handle location updates
// - collect key location day in NSDictionary
// - every N seconds send Network call to server to save (have tried 30 seconds, 15 minutes, 30 minute network intervals). Have also tried turning off network calls completely.
// deferred updates starter
if (!self.deferringUpdates) {
if([CLLocationManager deferredLocationUpdatesAvailable]){
[_locationManager allowDeferredLocationUpdatesUntilTraveled:500 timeout:(NSTimeInterval)120]; // (have also tried large numbers, and "Infinite"
self.deferringUpdates = YES;
NSLog(#"Deferred updates start");
} else {
NSLog(#"Deferred updates not available");
}
}
}
- (void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {
if(!error){
_deferringUpdates = NO;
NSLog(#"Deferred updates: finished");
} else {
_deferringUpdates = NO;
NSLog(#"Deferred updates: %#", [error localizedDescription]);
}
}
If the device is connected to a debugger or on a charger, the device will remain powered (not sleep) and therefore will not enter deferred mode. Deferred mode is a power optimization allowing the device to sleep. If the device is not scheduled to sleep for other reasons, enabling deferred mode will not force it to sleep otherwise. Try your test by ensuring no other apps are using location services, and disconnecting it from a charger with the screen off. After running for some time, plug back in and check your logs, you should see that the device slept and deferred updates.
From Apple's
allowDeferredLocationUpdatesUntilTraveled:timeout: documentation:
Deferred updates are delivered only when the system enters a low power
state. Deferred updates do not occur during debugging because Xcode
prevents your app from sleeping and thus prevents the system from
entering that low power state.
It is also worth noting that deferred updates are only available when locationManager.desiredAccuracy is set to kCLLocationAccuracyBest OR kCLLocationAccuracyBest; locationManager.distanceFilter must also be set to kCLDistanceFilterNone.
From Apple's documentation:
...the location manager allows deferred updates only when GPS hardware is available on the device and when the desired accuracy is set to kCLLocationAccuracyBest or kCLLocationAccuracyBestForNavigation.
and
...the distanceFilter property of the location manager must be set to kCLDistanceFilterNone.
I have been struggling with the same issue, and I may have found an answer that solves this problem for many - at least it solves my problem and gets deferred updates working consistently for me.
I followed all of the steps in this list and no matter what I did, location updates would not defer. It occurred to me that I might have other apps running that were not allowing the system to sleep, so I killed all other apps in the multitasking tray. I ran my sample app again and ... it worked!
But the story doesn't end there. I tried again a little later, and even though there were no other apps running in the multitasking tray I couldn't get location services to defer. Then it occurred to me that I have an app on my phone called "Moves" which manages to keep itself alive even after you manually kill it. I'm not entirely sure how Moves comes magically back to life when you kill it, but it does (perhaps using bluetooth and the app preservation / restoration service). Even though it is alive and tracking your location it doesn't appear in the multitasking tray. I think that only apps that are manually launched appear in the tray - if the OS launches an app it doesn't appear in the tray. But I digress ...
I was able to get deferred location services to work consistently in my app by disallowing Moves to use location services. When I did, Moves complained even though it wasn't in multitasking tray. It seems that if another app is using location services (and not deferring) your app won't defer either.
Hi recently with the iOS 9 GM seed version out,I have seen location update(allowDeferredLocationUpdatesUntilTraveled:timeout:) not getting deferred.The same code used to work in iOS 8.4 and below versions.Its draining my device's battery by a huge margin.
Is there anything we need to explicitly set or mention for iOS 9?Didn't find anything from Apple documentation
Here is the code that I implemented.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
if (!self.deferringUpdates) {
[self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:30];
self.deferringUpdates = YES;
} }
-(void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error { // Stop deferring updates self.deferringUpdates = NO;
}
I also set the allowsBackgroundLocationUpdates property but even that didn't help. self.locationManager.allowsBackgroundLocationUpdates=YES;
In iOS 9 and later, regardless of deployment target, you must also set the allowsBackgroundLocationUpdatesproperty of the location manager object to YES in order to receive background location updates. By default, this property is NO, and it should remain this way until a time when your app actively requires background location updates.
https://developer.apple.com/library/prerelease/ios/documentation/Performance/Conceptual/EnergyGuide-iOS/LocationBestPractices.html
Please let me know what additional I need to make
Thanks
I got my app rejected because
We found that your app uses a background mode but does not include functionality that requires that mode to run persistently. This behavior is not in compliance with the App Store Review Guidelines.
We noticed your app declares support for location in the UIBackgroundModes key in your Info.plist but does not include features that require persistent location.
I need to track the user location in the background in order to fire a notification if the user is near one of his/her pictures on the application. That's why I used background mode key on my .plist
This how I initialize my locationManager
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
_locationManager.distanceFilter = 1000; // Will update evert 1 KM.
_locationManager.activityType = CLActivityTypeFitness;
_locationManager.pausesLocationUpdatesAutomatically = YES;
[_locationManager startUpdatingLocation];
And I'm using didUpdateToLocation to receive the location on the background.
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
if (_currentLocation == nil){
self.currentLocation = newLocation;
}else{
if([self.currentLocation distanceFromLocation:newLocation]){
[self getNearPicturesMethod:newLocation];
}
}
}
They pointed to me that I should use "startMonitoringSignificantLocationChanges" but still if I remove the background key (what they want) the app won't receive location updates anymore if it's on the foreground, they present only if the user opens it and that doesn't work for me.
PS. I read related questions but nobody seemed to fix this problem or to provide a workaround.
Your app will wake on significant location changes. You do not need the background location mode key set for that. Same goes for region boundary monitoring.
For your case, I think you will more likely want to set regions for the nearest pictures and then just wait for boundaries to be crossed.
Use significant location changes to update the nearest pictures and reset your regions to those.
This will work as intended, be within the guidelines of Apple docs and impact the battery of the user very little, if any.
Testing Device: iPhone 5 (iOS 7)
I have an app that uses RegionMonitoring and updateLocation. If a region is entered, didEnterRegion is called as expected. Then I call startUpdatingLocation. But the method didUpdateToLocation is only called 10-20 times while it should update the location until the timer fires.
The relevant code:
CLLocationManager *_locationManager;
NSTimer *_timer;
-(void)initLocationManager
{
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
[_locationManager setActivityType:CLActivityTypeOther];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[_locationManager setPausesLocationUpdatesAutomatically:NO];
[_locationManager setDistanceFilter:kCLDistanceFilterNone];
}
//Did Enter Region, called as expected:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
[_locationManager startUpdatingLocation];
_timer = [NSTimer scheduledTimerWithTimeInterval:300.0f target:self selector:#selector(scheduleMethod:) userInfo:nil repeats:NO];
}
//Timer Fire Method:
- (void) scheduleMethod:(NSTimer*)timer
{
[Utils writeToLog:#"Timer-Stop"];
[_locationManager stopUpdatingLocation];
}
//This Method only called 10-20 Times (in the first 10-20 Seconds) and not the complete 5 Minutes:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
[Utils writeToLog:#"LocationUpdate!"];
}
So far I tried:
Restarting the Updates in the locationManagerDidPauseLocationUpdates method, but it seems that this is never be called:
-(void)locationManagerDidPauseLocationUpdates:(CLLocationManager *)manager
{
[WRUtils writeToLog:#"LocationUpdate paused, restarted"];
[_locationManager startUpdatingLocation];
}
Check for errors in the didFailWithError method, but this is not called either.
And played a bit with the properties:
[_locationManager setActivityType:CLActivityTypeOther];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[_locationManager setPausesLocationUpdatesAutomatically:NO];
Plist settings are right I guess:
Required background modes YES
Required background Modes
Item 0 App registers for location updates
How can I solve this?
Apple has introduced a new policy in iOS 7. iOS 7 does no longer deliver location updates in the background if you call „startUpdatingLocation“ while the App is in the background. You only get update when the App is brought to the foreground in this case.
When you’re using the geofencing feature, your App just gets a few seconds each time you get a RegionEntered/Exited notification to process this notification. And in these few seconds you may also get location updates. After these few seconds are over iOS 7 just suspends your App again.
You can use background task to get more than just a few seconds, but in iOS 7 Apple has also reduced the time Apps can run in the background from 10 minutes (iOS 6 and older) to only 3 minutes under iOS 7. It looks like that these 3 minutes are the total amount for the whole time the App is in the background. Which means you can not ask iOS 7 10 times to get 1 minute background time, you’ll only get 3 minutes in total, so after the 3rd time you’ve asked for a minute, your App won't get any background time anymore at all.
Your only chance in getting location updates in the background is to call „startUpdatingLocation“ while the App is in the foreground. This is sad especially when you only need location updates in response to Region(Enter/Exit messages, because you need to let the location updates running all the time. But you can at least reduce the battery usage by setting the accuracy value to kCLLocationAccuracyThreeKilometers when
you do not need location updates and set the accuracy to kCLLocationAccuracyBest only when you really need the geo coordinates. The iOS won’t power up GPS for the kCLLocationAccuracyThreeKilometers value, so the battery usage would be moderate in this case.
Also the value kCLLocationAccuracyBestForNavigation for the accuracy seems to cause problems under IOS 7. I do not get any location updates with this value if the device is not connected to a an external power supply.
All in all the new iOS 7 policy for location updates is making it much harder to develop certain kinds of Apps. Instead of registering for location updates only when needed, you are forced to register for these for the while lifetime of your App. Which of course drains the battery a little bit faster, though Apple’s intension for this new policy was probably the opposite.
UPDATE:
After some more testing I’ve found some kind of solution for the problem.
The docs from Apple mention that when using the Significant Location Change API, Apps can receive location updates in the background even if „startUpdatingLocation“ is started in the background.
My first tests didn’t work well. I was registering my App for significant location updates within the region monitoring delegate methods just before calling startUpdatingLocation (so this location service is only enabled when needed), but this still does not deliver location updates in the background as the docs would suggest.
But if you start listening for significant location changes directly after your App is launched (and never switch this off), you can call start „startUpdatingLocation“ while the App is in the background and also receive location updates in the background. The battery usage of having the "significant location change“ feature on all the time seems to be very low, so this will be probably the solution for most Apps.
You have to check if the „significant location change“ feature is available on the device, but it seems that all current devices do support this. Even the 5th generation of iPod Touch does support it (The iPod Touch can not use cell towers for location updates, which is the base method of this feature according to the docs from Apple, so I guess we can assume that all current devices running iOS 7 can use the „significant location update“ API. Though it’s probably a good idea to check if this feature is really available, maybe there are certain circumstances where the feature is not available).
Using the "significant location change“ API might have one disadvantage: The App can be launched in the background (if it was terminated in the background by the iOS to reuse its memory for other Apps) repeatedly and unnecessarily whenever the device has moved „significantly“ (according to the docs: when the cell tower has changed, but no more than once per 5 min). So Apps which only need to be activated, when a certain region is exited or entered, will be launched and informed about location changed all the time, not only at those regions. But I assume this should be still much better than having standard location updates active all the time.
My iPhone 5s drains the battery only 1% over night with the significant location changes active, instead of 12% with having the standard location updates active with the accuracy set to 3km.
Hope this helps all developers who are struggling with the new iOS 7 behavior.
In background timer will not run, so here your timer object will not respond, You need to create background task handler, check comment of below link,
How to run the timer in background of the application?
In background if you want to continue location service than you need to set pausesLocationUpdatesAutomatically flag, flag info on
pausesLocationUpdatesAutomatically flag info
if ([self.locationManager respondsToSelector:#selector(pausesLocationUpdatesAutomatically)]) {
self.locationManager.pausesLocationUpdatesAutomatically = NO;
}
check my comment on
Location service going to "Inactive" state in iPhone 5
For location manager there below are the CLLocationManagerDelegate methods for location update,
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation [Deprecated in __IPHONE_6_0]
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_6_0);
Where you found
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation ??
Use below code in your didEnterRegion Method to start updates again
[_locationManager startMonitoringSignificantLocationChanges];
if ([_locationManager respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)])
{
_locationManager.allowsBackgroundLocationUpdates =YES;
}
[_locationManager startUpdatingLocation];