8

I have an app which must report user location even if it backgrounded or even killed (terminated). The issue is that the app should report the location not rare than 1 hour interval.

I'm using significant location change (SLC) to track all movements which is quite enough when user is on the go but once user stops no further updates is raised and the app has no opportunity to submit new location (while user stays in the same area but without SLC).

To cover this case I start to use Background fetches to periodically send updates location in background even without SLC). The issue here is that background fetches used to work quite often (every 20-30 min since I used then in another iOS 7.x app) but now with iOS8 / iOS9 I can get it only once a day or so which is not acceptable in my case. I have executed ton of tests, developed simple background fetch app which add a local notification on fetch. No luck to force it to work more often.

Here is my AppDelegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
    [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
    return YES;
}

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{    
    [[UIApplication sharedApplication] cancelAllLocalNotifications];
    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
    NSDate *now = [NSDate date];
    localNotification.fireDate = now;
    localNotification.alertBody = [NSString stringWithFormat:@"Background fetch!"];
    localNotification.soundName = UILocalNotificationDefaultSoundName;
    NSInteger number = [UIApplication sharedApplication].applicationIconBadgeNumber;
    number++;
    localNotification.applicationIconBadgeNumber = number;
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];

    completionHandler(UIBackgroundFetchResultNewData);
}

All what is done here is to add local notification on every background fetch. I always finalize the background execution with UIBackgroundFetchResultNewData.

Do you have any suggestions on how to force background fetches to work more often (or prove links that it is not possible any more)? Any alternative solutions to meet my requirement are also welcomed!

Mando
  • 11,414
  • 17
  • 86
  • 167
  • 1
    Background fetch has never had a guaranteed execution period. I suggest you send silent pushes to your device to give it a chance to report location, but this won't work if the app is terminated – Paulw11 Sep 20 '15 at 21:49
  • It was never guaranteed but at least it used to work more often. Were there some significant changes since iOS 7 related to background fetches? – Mando Sep 20 '15 at 22:33
  • 1
    What value to you return to the completion handler? New data or no new data? In my experience even on iOS8 it wasn't called every 20-30 minutes. – Paulw11 Sep 20 '15 at 22:45
  • I always finalize the background execution with UIBackgroundFetchResultNewData. I have just updated the question with sample code. It is really short and simple app which reproduces the issue on different devices and different ios (ios8/9). How often you have it called? @Paulw11 – Mando Sep 20 '15 at 23:44
  • 2
    If you only need SLCs and the phone didn't move significantly in the last hour, can't you assume it didn't move at all? While this doesn't has to be the case, as soon as it does move significantly you would get notified again. – HAS Sep 29 '15 at 06:17
  • 1
    Phone could be dead and moved than charged while backend is sure that user is in wrong location – Mando Sep 30 '15 at 01:32

2 Answers2

4

It turned out that background fetches in iOS depend a lot on what you are doing within the handler, especially network activity. Here the list of dependencies which you should consider trying to understand if and how often iOS will execute your fetch:

  • time you spend in handler
  • result (NoData, NewData)
  • error handling (you will be launched less likely if you code crashes
  • timeouts (your code execution could be interrupted by iOS)
  • power usage
  • network activity related to result (you MUST do a network request when saying that you have NewData, otherwise your fetch could be executed next time in a day or so never.

Apps that download small amounts of content quickly, and accurately reflect when they had content available to download, are more likely to receive execution time in the future than apps that take a long time to download their content or that claim content was available but then do not download anything.

The last item turned out to be crucial in my case, because for testing purposes I declared fetches and never tried to download anything. Once I started to use network in the handler, background fetches continue to work as expected every 15-30 minutes.

Apple docs: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html

Mando
  • 11,414
  • 17
  • 86
  • 167
3

I worked on a similar project. After trying various solutions, the only way I've found is the solution proposed here: https://github.com/voyage11/Location

Here is the tutorial corresponding to the git: http://mobileoop.com/getting-location-updates-for-ios-7-and-8-when-the-app-is-killedterminatedsuspended

All credits to https://stackoverflow.com/users/1995940/ricky

It's saved me a lot of time!

Community
  • 1
  • 1
Alban
  • 1,624
  • 11
  • 21