35

I've recently upgraded my iOS devices to use iOS 7. One of the apps that we're developing uses background location services to track device location and all of our testers have reported that the app no longer appears to track in the background under iOS 7.

We have verified that backgrounding for the app is enabled in the settings on the device and the previous build worked flawlessly under iOS 6. Even if the device were cycled, the app would restart after a location update.

Is there something else that needs to be done to make this work under iOS 7?

mfaani
  • 33,269
  • 19
  • 164
  • 293
Alan Samet
  • 1,118
  • 2
  • 15
  • 18

7 Answers7

47

Here is the solution that I used to get continuous location from iOS 7 devices no matter it is in foreground or background.

You may find the full solution and explanation from blog and also github:-

  1. Background Location Update Programming for iOS 7 and 8

  2. Github Project: Background Location Update Programming for iOS 7 and 8

Methods and Explanation:-

  1. I restart the location manager every 1 minute in function didUpdateLocations

  2. I allow the location manager to get the locations from the device for 10 seconds before shut it down (to save battery).

Partial Code Below:-

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{

for(int i=0;i<locations.count;i++){
    CLLocation * newLocation = [locations objectAtIndex:i];
    CLLocationCoordinate2D theLocation = newLocation.coordinate;
    CLLocationAccuracy theAccuracy = newLocation.horizontalAccuracy;
    NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];

    if (locationAge > 30.0)
        continue;

    //Select only valid location and also location with good accuracy
    if(newLocation!=nil&&theAccuracy>0
       &&theAccuracy<2000
       &&(!(theLocation.latitude==0.0&&theLocation.longitude==0.0))){
        self.myLastLocation = theLocation;
        self.myLastLocationAccuracy= theAccuracy;
        NSMutableDictionary * dict = [[NSMutableDictionary alloc]init];
        [dict setObject:[NSNumber numberWithFloat:theLocation.latitude] forKey:@"latitude"];
        [dict setObject:[NSNumber numberWithFloat:theLocation.longitude] forKey:@"longitude"];
        [dict setObject:[NSNumber numberWithFloat:theAccuracy] forKey:@"theAccuracy"];
        //Add the vallid location with good accuracy into an array
        //Every 1 minute, I will select the best location based on accuracy and send to server
        [self.shareModel.myLocationArray addObject:dict];
    }
}

//If the timer still valid, return it (Will not run the code below)
if (self.shareModel.timer)
    return;

self.shareModel.bgTask = [BackgroundTaskManager sharedBackgroundTaskManager];
[self.shareModel.bgTask beginNewBackgroundTask];

//Restart the locationMaanger after 1 minute
self.shareModel.timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self
                                                       selector:@selector(restartLocationUpdates)
                                                       userInfo:nil
                                                        repeats:NO];

//Will only stop the locationManager after 10 seconds, so that we can get some accurate locations
//The location manager will only operate for 10 seconds to save battery
NSTimer * delay10Seconds;
delay10Seconds = [NSTimer scheduledTimerWithTimeInterval:10 target:self
                                                selector:@selector(stopLocationDelayBy10Seconds)
                                                userInfo:nil
                                                 repeats:NO];
 }

Update on May 2014: I got a few requests for adding sample codes on sending the location to server for a certain time interval. I have added the sample codes and also combined a fix for BackgroundTaskManager to solve a glitch for background running over an extended period of time. If you have any questions, you are welcomed to join us for a discussion here: Background Location Update Programming for iOS 7 with Location Update to Server Discussion

Update on January 2015: If you want to get the location update even when the app is suspended, please see: Get Location Updates for iOS App Even when Suspended

mfaani
  • 33,269
  • 19
  • 164
  • 293
Ricky
  • 10,485
  • 6
  • 36
  • 49
  • updates to iOS 7.1 do not require these work-arounds, correct? – wprater May 03 '14 at 17:33
  • I am not sure. I haven't try the old solution on iOS 7.1. I will test that out when I have time. – Ricky May 03 '14 at 23:49
  • @Ricky What if we use Background Fetch mechanism to wake application when it's not running? I dont know whether it's acceptable by apple to use BackgroundFetch to get location details. – Azhar Bandri Sep 04 '14 at 12:08
  • Yes, Background Fetch is possible and I believe it is acceptable by Apple. The solution that I provided is only good for the apps that need constant location update for every N minutes. If your app does not need the frequent location update, Background Fetch request is actually a better solution. – Ricky Sep 04 '14 at 13:15
  • Is it possible to start the location manager in the background when using background fetch? Then that would be a better solution for me than updating the location every minute.. – wouter88 Sep 08 '14 at 13:04
  • 1
    @Ricky Hey Rickey I like your code and I downloaded and tested your github project, but can you please add a tutorial about adding your code to anyones else's project? – Michael Choi Oct 14 '14 at 11:49
  • @MichaelChoi Yes, I will do that when I have time. I will add additional instructions and also the Cocoapod on the Github project. – Ricky Oct 23 '14 at 00:16
  • 1
    I love u man :) I have your downloaded code and used your classes with little change in my AppDelegate. It is working like a charm Thank you so much – Hamza Hasan Nov 10 '14 at 06:40
  • Is there a reason why you're scheduling in didUpdateLocations rather than restartLocationUpdates? I would think a pattern like: 1. Schedule restartLocationUpdates. 2. At the end of restartLocationUpdates schedule another call of restartLocationUpdates. I'd think this pattern may be safer because you wouldn't have to rely on didUpdateLocations being called. On a related noted, since you're relying on didUpdateLocations being called, should you also reschedule restartLocationUpdates in the didFailWithError delegate? – Steven Wexler Jun 22 '15 at 20:15
  • iOS 9 kills it off after 3 minutes, but you can fix it with a quick code addition that I found in this comment: http://mobileoop.com/background-location-update-programming-for-ios-7#comment-433 – smileham Oct 19 '15 at 03:46
  • could you help me to make it swift – cristan lika Dec 14 '16 at 19:03
18

If you look at WWDC 2013 Session video #204 - What's new with multitasking pdf, page number 15 clearly mentions that apps wont launch in the background if user kills it from the app switcher. Please see the image,

enter image description here

Obj-Swift
  • 2,802
  • 3
  • 29
  • 50
15

I think they made an optimization (probably using motion sensors), to detect "relatively" stationary positioning of the phone and they stop the location updates. This is only a speculation, but my tests currently show:

  1. Starting location updates; (tested with accuracy of 10 and 100 meters, 3 times each)
  2. Turn device's screen off to put the app in the background;
  3. Leave the device stationary (e.g. on a desk) for 30 min.

The data I log shows the geo-updates stop coming after ~ 15m and 30s. With that all other background processing you do is also terminated.

My device is iPhone 5 with iOS 7.

I am 95% sure, this wasn't the case on iOS 6/6.1. Where getting geo updates with 100m accuracy used to give you pretty much continuous running in background.

Update

If you restart the location manager every 8 minutes, it should run continuously.

Update #2

I haven't tested this in latest, but this is how I restarted it when I wrote the post. I hope this is helpful.

- (void)tryRestartLocationManager
{
    NSTimeInterval now = [[NSDate date] timeIntervalSince1970];

    int seconds = round(floor(now - locationManagerStartTimestamp));

    if ( seconds > (60 * 8) ) {
        [locationManager stopUpdatingLocation];
        [locationManager startUpdatingLocation];
        locationManagerStartTimestamp = now;
    }
}
Nikolay Tsenkov
  • 1,128
  • 10
  • 26
  • 1
    This appears to be in-line with what I've observed. So long as I'm moving the app stays active, but if I'm stationary for an extended period of time, it's like CoreLocation ignores the pausesLocationUpdatesAutomatically setting and never resumes. – Alan Samet Sep 24 '13 at 21:41
  • Exactly. If it stops the updates, it's like, they are never resumed. – Nikolay Tsenkov Sep 25 '13 at 04:44
  • 3
    @NikolayTsenkov could you post a code snippet? I'm finding that if I restart location manager in the background, the task is eventually killed when `backgroundTimeRemaining` reaches zero. – pcoving Sep 28 '13 at 22:08
  • @NikolayTsenkov, thanks for the tips. I did a restart of location manager every minutes, the app able to report our user's location continuously. However, I found out that using this approach, the phone battery die faster then expected.. i.e before the changes, Our test phones (iPhone 5, 4S) can run from 8am till 10pm, After the changes, the battery die at 4:00pm...is this the same results on your end? – James Khoo Oct 23 '13 at 10:54
  • My tests show 10% per hour with AccuracyBest, 5% per hour AccuracyHundredMeters. Tested with iPhone 5. – Nikolay Tsenkov Oct 24 '13 at 06:40
  • @NikolayTsenkov: can you please upload some code or example regarding of it. –  Feb 03 '14 at 11:17
  • @Optimistic, I've included a snippet now. I hope this is helpful. – Nikolay Tsenkov Feb 03 '14 at 13:38
  • @NikolayTsenkov: Thank you very much for your update,but can you tell me where to call this method, have i call it to in NSTimer ? It would be great if you post that line too. –  Feb 04 '14 at 12:56
  • @Optimistic, I am using CoreMotion to track some of the device's sensors. I have a block which is called with the changes and that's when I call tryRestartLocationManager. Simply said, I execute continuously something else (haven't tried with a timer, but it should work) and I constantly call this method. Timer is a better option and if nothing related changed in iOS it should be working. – Nikolay Tsenkov Feb 05 '14 at 08:25
  • @NikolayTsenkov: Thnks for reply again :) . i have one question can i call `[locationManager startUpdatingLocation];` in backgroud , when pushnotification is called? –  Feb 05 '14 at 09:13
  • @Optimistic, not sure. My guess is that you have to start it in the foreground and then you can restart in a background task. – Nikolay Tsenkov Feb 05 '14 at 09:29
  • I found a solution that might help both of you. You can take a look at the answer: http://stackoverflow.com/questions/18946881/background-location-services-not-working-in-ios-7/21966662#21966662 – Ricky Feb 23 '14 at 09:54
  • Have you tested this with iOS 7.1? – Markus Rautopuro May 21 '14 at 07:40
  • Its ok when the location updates stop when the device does not move, this always was the case since ios5. Did the locations service restart providing locations when you move again, or was the location service down forever? – AlexWien Jul 15 '14 at 21:09
7

One of my iOS app needs to send location update to server at regular intervals and following approach worked for us....

Starting in iOS 7:

  • an app must have already been using location services (startUpdatingLocation) BEFORE having been backgrounded in order for the eligible for background run time
  • the background grace period timeout was reduced from 10 minutes to 3 minutes
  • stopping location updates (via stopUpdatingLocation) will start the 3 minute backgroundTimeRemaining count down
  • starting location updates while already in the background will not reset the backgroundTimeRemaining

So, DO NOT STOP the location updates anytime...Instead, prefer to use the necessary accuracy (fine location vs coarse location). Coarse location does not consume much battery...so this solution solves your problem.

After much searching online, found a link which provide a viable solution for iOS 7. The solution is as follows:

  • While the app is still in the foreground, start location updates (startUpdatingLocation) but set the accuracy and distance filters to very course-grained (e.g. 3 km) updates. It is important to do this in the foreground (in applicationDidEnterBackground is too late).
  • When a fine-grained resolution is required, temporarily set the accuracy and distance filters to get the best possible location, and then revert them back to the course-grained values – but never stop location updates.
  • Because location updates are always enabled, the app will not get suspended when it goes to the background.

And make sure you add the following to your application’s Info.plist “location” to the UIBackgroundModes key “location-services” and “gps” to the UIRequiredDeviceCapabilities key

Credits: http://gooddevbaddev.wordpress.com/2013/10/22/ios-7-running-location-based-apps-in-the-background/

androidExplorer
  • 126
  • 1
  • 6
5

I found another thread Start Location Manager in iOS 7 from background task Sash mentioned that

I found the problem/solution. When it is time to start location service and stop background task, background task should be stopped with a delay (I set 1 second). Otherwise location service wont start.

Can anyone try that and verify?

Nikolay, can you paste your code here? I tried to restart the location manager every 8 minutes but it does not run continuously.

Update:

After searching High and Low, I found the Solution from Apple Forum!

In iOS 7, you can not start the location service in background. If you want the location service to keep running in the background, you have to start it in foreground and it will continue to run in the background.

If you were like me, stop the location service and use timer to re-start it in the background, it will NOT work.

For more detailed information, you can watch the first 8 minutes of video 307 from WWDC 2013: https://developer.apple.com/wwdc/videos/

Feb 2014 Update: I can get the location continuously from device using iOS 7 after several months of trying. You may see the full answer here: Background Location Services not working in iOS 7

Community
  • 1
  • 1
Ricky
  • 10,485
  • 6
  • 36
  • 49
  • Sorry, I haven't seen there is any development in this thread (why not shoot me an email, stackoverflow?).I am starting it in the foreground. Still, I am restarting it in the background, every 8 minutes. – Nikolay Tsenkov Oct 02 '13 at 05:08
  • When you say "restarting in the background", do you mean calling startUpdatingLocation/stopUpdatingLocation? – deruse Oct 09 '13 at 04:03
  • Sorry guys, could you help me to know how to start the location services in foreground?? What I need to do? I really will appreciate your help. Thanks – Guerrix Oct 23 '13 at 16:28
  • can we change desired accuracy or distanceFilter when app goes to background? – Toseef Khilji Oct 25 '13 at 06:47
  • 6
    Guys, after more than 2 months of trying many different types of solutions. I found one very good solution which consume less than 5% of battery per hour. I am going to share the solution with all of you in a few days. – Ricky Nov 11 '13 at 02:04
  • @voyage11 did you posted your solution? I also work on this topic and can't resolve it. This is my question: http://stackoverflow.com/questions/21220245/location-update-in-background-and-suspended-app-modes – Tatiana Jan 20 '14 at 11:15
  • @voyage11: it would be great if you post your solution. –  Feb 03 '14 at 11:20
  • I have never understood your background problems. my app runs in background getting one location per second. It never stoped recording locations (I tested it while I am moving, hiking). (iphone 4, ios 6, ios 7.1, AccuarcyBest). Any tipps how to test wheter it happens at my app too? – AlexWien Jul 15 '14 at 21:04
  • Is your app sending the location coordinate every N minute to your server? If it is, you may try this. Run your app in the background for 30 minutes - 1 hour. Check the server database to see if you have the location for every N minute. If it stops sending the location after some time, it means that your app has been terminated in the background. – Ricky Jul 16 '14 at 02:11
  • If you need to be sure that your location will not be stopped by system after 15 minutes in background then you can implement it like here: http://stackoverflow.com/a/28373247/2102779 – Leszek Szary Feb 07 '15 at 20:32
1

Is the icon on the status bar turned on? It's a strange behaviour I had too. Check my question: startMonitoringSignificantLocationChanges but after some time didUpdateLocations is not called anymore

I discovered that the significant location changes was on but simply stopping and restarting the service (for significant changes) was not firing new locations.

Community
  • 1
  • 1
Mr. Cobblepot
  • 141
  • 1
  • 6
0

You must set pausesLocationUpdatesAutomatically property of CLLocationManagerclass to false to disable system from pausing the location updates.

Tibin Thomas
  • 1,365
  • 2
  • 16
  • 25