24

I know that if an app uses "The significant-change location service", iOS will wake it up if there's a location update to be delivered, even if the app is terminated.

I couldn't find a clear answer about the case if the app is using standard location services and specifies location as the key for UIBackgroundModes: Will iOS also wake it up to deliver the update even if it's terminated? Or does the app need to be running in the background to get the location update callback?

UPDATE: At the time I was asking this, I didn't have the time to test it. But after I got an answer here, I wrote this piece of code in my application's delegate to see if my terminated app will be relaunched when it gets a location update. I'm displaying a UILocalNotification when I'm notified of the update. However, when I terminated my app and then changed my location in the city, the app was not relaunched and I didn't get any updates. Can you tell me what it's that I'm doing wrong?

UPDATE #2: According to the final findings in this Q&A, there's nothing wrong with this code and it's the expected behaviour for an app that uses standard location services, not to be relaunched after termination.

I've added location as one of the UIBackgroundModes in the Info.plist file.

And this is the location related parts of my app delegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

    [UIApplication sharedApplication].applicationIconBadgeNumber = 0;

    m_locManager = [[CLLocationManager alloc] init];
    m_locManager.delegate = self;
    [m_locManager startUpdatingLocation];
    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    [m_locManager startUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    NSLog("%@", [NSString stringWithFormat:@"Background Fail %@", [error localizedDescription]]);
}

- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation
{
    UILocalNotification * theNotification = [[UILocalNotification alloc] init];
    theNotification.alertBody = [NSString stringWithFormat:@"Background location %.06f %.06f %@" , newLocation.coordinate.latitude, newLocation.coordinate.longitude, newLocation.timestamp];
    theNotification.alertAction = @"Ok";

    theNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];

    [[UIApplication sharedApplication] scheduleLocalNotification:theNotification];
}
aslı
  • 8,740
  • 10
  • 59
  • 80
  • 2
    I didn't know the trick it could be woke up an app, upvoted –  Sep 02 '12 at 22:01
  • Again, it CANNOT be used as a trick to wake up your app periodically! Apple will instantly reject your app if you use UIBackgroundModes for no other use than its purpose: *Tracking a User's Location*. – runmad Sep 03 '12 at 03:20
  • @runmad Yes I know that this should be used for the right purpose. Mine is to track the user's location in order to send it to the server, so that the server will send him a push notification if he's closer to an area. Considering app rejection, will I be safer if I use significant change location service instead of the standard one? – aslı Sep 03 '12 at 06:16
  • Based on the comment by @matheszabi I was just making sure that others reading this wouldn't try and use this solution to "cheat" and end up getting rejected :) I personally wouldn't want an app to re-launch all the time in the background when I change location, just because it needs to do something weird. – runmad Sep 04 '12 at 02:52

3 Answers3

5

Looks like you found a workable solution and it seems like using the significant change framework would save you some battery life, so long as it is accurate enough for your purposes. Your solution is also beneficial because it allows you to juggle potentially more than the per-app limit of 20 regions allowed to be simultaneously monitored, by only monitoring those regions that you are closest to.

I need similar functionality, but don't think the significant change would be accurate enough for my app, so I dug around some more and found the following.

Per the Location Awareness Programming Guide, under "Monitoring Shape-Based Regions":

In iOS, regions associated with your app are tracked at all times, including when your app is not running. If a region boundary is crossed while an app is not running, that app is relaunched into the background to handle the event. Similarly, if the app is suspended when the event occurs, it is woken up and given a short amount of time to handle the event.

And also in "Handling Boundary-Crossing Events for a Region":

Every time the user’s current location crosses a boundary region, the system generates an appropriate region event for your app. If your app is already running, these events go directly to the delegates of any current location manager objects. If your app is not running, the system launches it in the background so that it can respond. Apps can implement the following methods to handle boundary crossings:

locationManager:didEnterRegion:

locationManager:didExitRegion:

In my case, I just needed to fire a notification when the boundary was crossed, so I set my AppDelegate to conform to CLLocationManagerDelegate, created a locationManager property, and put this in my implementation file:

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.locationManager.delegate = self;
    return YES;
}

- (CLLocationManager *)locationManager
{
    if (!_locationManager) {
        _locationManager = [[CLLocationManager alloc] init];
        _locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    }
    return _locationManager;
}

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
    UILocalNotification *notification = [[UILocalNotification alloc] init];
    notification.alertBody = NSLocalizedString(@"You crossed a boundary!", @"user crossed a boundary");
    [[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}

Testing shows this works without the UIBackgroundModes location key needing to be set. In this use-case, the OS handles boundary monitoring for you and then sends the event to your app just long enough to process the event. This doesn't launch the app, it just runs it in the background for a few seconds to process the boundary event, during which time you can only do relevant activities. Hope this helps someone else, too!

Daniel J
  • 446
  • 6
  • 8
  • Hi Daniel, In my case I have to perform multiple API calls. I think when user cross any region, app wakes up for few seconds, not sufficient to perform my tasks. I have checked this with local notification on crossing region. Is this possible to extend app wakeup time some how? Thanks in advance. – Yogendra Mar 08 '17 at 14:25
4

Yes, it will behave the same way except it will receive updates more often (and thus consume more battery).

Source: App States and Multitasking under the section Tracking the User's Location.

EDIT After re-reading, it seems that the program will be woken up from a suspended state but not a terminated one. I don't think you really need this background mode, though. It is designed for apps that need fine location info (like turn-by-turn navigation). Check out the section in the Location Programming guide about "region based" programming. The example in your comments is listed as one of the uses of this app.

EDIT AGAIN As per the discussion in the comments, the final solution seems to be setting significant location change until the user is close enough, and then switching over to fine location change (and hoping the app doesn't get terminated during that time ^^)

borrrden
  • 33,256
  • 8
  • 74
  • 109
  • To extend this answer, you cannot use this background mode to make your app launch for other reason that if it fact needs the users location to perform an update of some kind. – runmad Sep 03 '12 at 02:07
  • Quite correct @runmad Apple will reject apps that behave this way. – borrrden Sep 03 '12 at 02:41
  • I wrote a test code to see this in action. The app doesn't get relaunched that way, I don't know if it's something that I'm doing wrong or it's the expected thing. Check my question if you want to have a look at my steps in detail. – aslı Sep 04 '12 at 12:16
  • Did you also test it with the significant location change mode? – borrrden Sep 05 '12 at 01:22
  • @borrrden Yes, I did. It gets relaunched after termination with the significant location change mode, but not with the standard location mode (with UIBackgroundMode set as location) – aslı Sep 07 '12 at 08:35
  • I read the relevant section again, and I think I misunderstood it the first time. See my edit. – borrrden Sep 07 '12 at 08:45
  • @borrrden Yes, you're right that this UIBackgroundMode setting just wakes the app from a suspended state. I cannot use region monitoring, because it's supported by limited iOS devices right now due to its hardware constraints (see http://stackoverflow.com/questions/12243051/on-which-ios-devices-exactly-is-region-monitoring-supported) So, my plan is to start with significant mode, then when the user gets closer to the area, switch to the standard mode to get more updates for a finer location info. – aslı Sep 07 '12 at 09:56
  • Be aware that significant change is also limited. Only devices that have a cellular radio connection can use it. – borrrden Sep 07 '12 at 09:58
  • @borrrden Yes, I'm keeping that in mind also. Thanks for all your answers, btw. If you can edit your answer so that it reflects our final conclusions, I can mark it as the accepted answer. – aslı Sep 07 '12 at 13:17
  • Hi Borrrden, In my case I have to perform multiple API calls inside didEnterRegion. I think when user cross any region, app wakes up for few seconds, not sufficient to perform my tasks. I have checked this with local notification on crossing region. Is this possible to extend app wakeup time some how? Thanks in advance. – Yogendra Mar 08 '17 at 14:32
4

Once you kill the app, the location updates are sent to application

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

You need to collect the location updates inside this delegate method by looking for the launch key as below,

if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey])
{
//Code to handle the location update
}

Hope this is what your looking for. Yes you need to set the key "Required backround modes" with value "App registers for location updates" in plist.

KrishnaKumar
  • 188
  • 10