8

It's surprisingly difficult to find a definitive answer to this; couldn't find it mentioned in the Apple documentation and couldn't find a definite yes/no after searching past questions.

The question is simple - if the app requests a background fetch to be performed after N time, then the user terminates the app. Will the OS still launch the app into the background to perform the background fetch?

shim
  • 7,170
  • 10
  • 62
  • 95
Gruntcakes
  • 36,166
  • 41
  • 170
  • 346
  • Shouldn't this be done by registering some sort of service? I mean, when an app is terminated, it *should* not be executing anymore. – Xorifelse Jun 29 '17 at 14:45
  • @Xorifelse, no offense, but do you actually know anything about iOS? There are no such things as services in iOS. And yes of course it is not executing after termination, but the OS can launch the app under some circumstances. And the question is does the OS do that for this situation, its not documented if it does or if it does not. – Gruntcakes Jun 29 '17 at 14:46
  • A little, played around with it but I was using logic. In a way the `Background Fetch` is a service. Secondly I found [this](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623125-application) and under discussion it says: `If you do not call the completion handler in time, your app is terminated.` which is about +/- 30 seconds max. When the app is terminated, don't expect the handler to be called again unless you start the app. – Xorifelse Jun 29 '17 at 14:59
  • If you the user terminates your app then it won't be woken up in the background outside of specific cases (just VoIP apps I think) – dan Jun 29 '17 at 15:00
  • @Xorifelse "When the app is terminated, don't expect the handler to be called again unless you start the app" But the documentation doesn't say it *won't" be called again, it says "If your app takes a long time to call the completion handler, it may be given fewer future opportunities to fetch data in the future." Thats the problem - the documentation isn't clear, and neither it seems is tribal knowledge - there's lots of contradictions. – Gruntcakes Jun 29 '17 at 15:06
  • Perhaps [this answer](https://apple.stackexchange.com/q/252239) will provide some celerity. – Xorifelse Jun 29 '17 at 15:17
  • @Xorifelse No. that question is just general stuff about background execution. This question is about a very specific instance of background execution. – Gruntcakes Jun 29 '17 at 15:48
  • @dan according to this it will: http://www.techotopia.com/index.php/IOS_7_Multitasking,_Background_Transfer_Service_and_Fetching#An_Overview_of_Background_Fetch – Gruntcakes Jun 29 '17 at 16:17
  • Read the "Understanding When Your App Gets Launched into the Background" section of: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html#//apple_ref/doc/uid/TP40007072-CH4-SW1 – dan Jun 29 '17 at 16:54
  • @dan and I quote directly from WWDC 13 Whats new in multitasking ".. once your application does get launched in the background .. didFinishLaunching gets called. If your application was already running your application will be resumed ...". So this is directly stating it will be launched if it was not in the background. – Gruntcakes Jun 29 '17 at 17:04
  • What does that have to do with the user terminating your app? I quote directly from the document I just linked you: "If an app is terminated for any reason other than the user force quitting it, the system launches the app when one of the following events happens:" and "In most cases, the system does not relaunch apps after they are force quit by the user. One exception is location apps,..." – dan Jun 29 '17 at 17:08
  • 1
    @dan there are only two ways the app could be terminated, if the user did so or if the OS did so. The WWDC video does not make a distinction between the two. The point is you do not know *definitively*, you are quoting documentation which may be out of date or incomplete. I also do not know definitely, I am merely pointing out contradictions in sources. The whole point of this question to find out definitely. Do you 100%, definitely, for an absolute fact, without equivocation know the answer or not? Not quoting documentation - an experienced iOS dev knows documentation is not totally reliable. – Gruntcakes Jun 29 '17 at 17:13
  • You're welcome to test it yourself in your app. As an experienced iOS dev, that's what I would do if I didn't want to trust the documentation – dan Jun 29 '17 at 17:16
  • @dan now you're just got tetchy, you could have been civil and just admitted you didn't know. I am attempting to experiment, but given the nature of this functionality that is not something you can do and reach a definite conclusion quickly or easily. Have a nice day. – Gruntcakes Jun 29 '17 at 17:19

3 Answers3

6

Okay, once again background modes cause confusion. No offense to the other people trying to help, but this is more complicated than it seems.

First of all: This is out of date, as Sausage guessed in the comments. I know this for a fact, because the section about VoIP apps is still explaining the "old way" to do this, with a handler that gets called periodically. I investigated this a bit for this answer, so I suggest you go and read that. The important lesson for this case here is that iOS makes a distinction between an app being terminated by the user or by the system, plus it also plays a role whether the phone was rebooted or not.

So to sum this (and your question) up you basically want to know whether this part of the above, outdated documentation is still correct word for word:

In most cases, the system does not relaunch apps after they are force quit by the user. One exception is location apps, which in iOS 8 and later are relaunched after being force quit by the user. In other cases, though, the user must launch the app explicitly or reboot the device before the app can be launched automatically into the background by the system. When password protection is enabled on the device, the system does not launch an app in the background before the user first unlocks the device.

Apple: Understanding When Your App Gets Launched into the Background

I thoroughly investigated the rest of the docs, but did not find any definite answer, so it unfortunately boils down to what dan already suggested: Test it. My gut feeling is that the documentation is still correct in that regard, though (as said what's not is the VoIP stuff). I say that because the UI in the Settings app calls the feature "Background App Refresh", so users are probably supposed to understand that an app having this permission won't refresh when they "push" them out of background (i.e. home button -> swipe it out). For regular users, apps are either quit (not in the task manager at all), in the foreground (using them) or in background (they're in the task manager and another app is in foreground and/or the phone is locked).


To really test this you'd have to write an app and actually carry it around a bit (I assume at least two days) in each condition. First while it is in background (the OS should periodically let it fetch, as you probably know this can also be triggered in Xcode) and then while it is force-quit. The problem is to verify that it fetched stuff. I'd go with a logfile that can be shared via iTunes. I have typed up some code for this:

-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    NSLog(@"We're awake! Booyah!");
    
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:config
                                                          delegate:nil
                                                     delegateQueue:[NSOperationQueue mainQueue]];
    NSMutableURLRequest *request = [NSMutableURLRequest new];
    request.HTTPMethod = @"GET";
    request.URL = [NSURL URLWithString:@"https://www.google.com"];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData * _Nullable data,
                                                                NSURLResponse * _Nullable response,
                                                                NSError * _Nullable error) {
                                                NSDate *now = [NSDate date];
                                                NSString *toLog = [NSString stringWithFormat:@"%@ - fetched\n",
                                                                   [now description]];
                                                [self updateTestDocumentWithString:toLog];
                                                NSLog(@"Yay, done!");
                                                completionHandler(UIBackgroundFetchResultNewData);
                                            }];
    [task resume];
}

- (void)updateTestDocumentWithString:(NSString *)toAppend {
    NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    NSString *filePath = [[docDir stringByAppendingPathComponent:@"logfile.txt"] copy];
    if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
        if (![[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]) {
            NSLog(@"We're effed...");
            return;
        }
    }
    NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
    if (!file) {
        NSLog(@"We're effed again...");
        return;
    }
    [file seekToEndOfFile];
    // ensure this is never nil
    [file writeData:[toAppend dataUsingEncoding:NSUTF8StringEncoding]];
    [file closeFile];
}

This would go into the app delegate, and don't forget to add the Application supports iTunes file sharing boolean setting in your app's plist. I will leave this running on my development device for a bit and check the logfile, eventually reporting back here. Feel free to test it yourself, too.

Community
  • 1
  • 1
Gero
  • 3,689
  • 16
  • 33
  • 1
    Update: So far my small test app has not gotten the chance to fetch, but I had to leave the test device in the office in airplane mode for a while. :/ I'm, however, beginning to suspect debug builds get very low priority for this, if any at all (expecting for debugging you will trigger a fetch event via Xcode anyways). I'll try again on Monday. Dang, I can in part understand why Apple keeps the criteria the OS uses to allow apps to fetch confidential, but a *little* more documentation here would be nice... :/ – Gero Jul 07 '17 at 17:43
  • Update 2: More evidence for my hunch that debug installs from Xcode don't get any chance to `performFetch...`: It's been several days now and although the device was in Airplane mode sometimes it had plenty of time to call the app (which was in the background, i.e. I didn't even get to test case 2: device rebooted...). I'm still sure force-quitting won't let you fetch, while getting terminated via a device reboot or system memory stuff *will* let you (i.e. it will relaunch your app). I'm gonna check the dev forums probably and come back here eventually. :/ Sorry I have nothing definitive yet. – Gero Jul 11 '17 at 06:35
  • I'm confused was my answer correct or not? Or since you weren't able to install with a release build you can't say for sure? 2. Isn't the frequency of background fetch relative to the user's usage? I mean you didn't use the device much so perhaps it thought it shouldn't fetch anything... – Honey Jul 11 '17 at 22:05
  • @Honey No no, I still think you and I are correct. I agree, the reason why I probably didn't get any fetch calls yet is due to device usage (and *perhaps* because it is a debug build and/or a very simple test app with basically zero UI and actual usefulness). After all, Apple didn't disclose how exactly the OS determines when it lets an app fetch. I installed the test on a development device that needs to stay in my office, so it's not realistically used. I might try to remedy that and test on my own, personal device (with a better test app, too), but atm I am short of time to do so. – Gero Jul 12 '17 at 06:30
  • Just as a heads-up on this (by now ancient) question: I said I would try this out further and I did, but so far never got any fetches, even while the app was just suspended. On the app dev forums I found [this](https://forums.developer.apple.com/message/101991#101962) among several other hints that, indeed, Xcode and debug builds might play a role for things like this. I will eventually try to test this under as close-to-release conditions as possible, but this will take me some time and effort. – Gero Aug 09 '17 at 12:36
2

EDIT:

https://devforums.apple.com/message/873265#873265 (login required)

Also keep in mind that if you kill your app from the app switcher (i.e. swiping up to kill the app) then the OS will never relaunch the app regardless of push notification or background fetch. In this case the user has to manually relaunch the app once and then from that point forward the background activities will be invoked. -pmarcos

That post was by an Apple employee so I think i can trust that this information is correct.


OLD answer:

According to this answer wrote by a top user: iOS background fetch: your app won't be woken up again.

Make sure you're not killing the app (i.e. by double tapping on the home button and swiping up on your app for force the app to terminate). If the app is killed, it will prevent background fetch from working correctly.

It really doesn't make sense for it to be woken up...it kinda invalidates the user killing the app.

Having that said there are different ways a terminated/force quit app can be launched again:

  • Tapping on a notification.
  • Tapping on the app icon.
  • Using openUrl to open your app from another app.
  • If you use PushKit...then your app would be launched. Imagine if had a VOIP app e.g. Skype, WhatsApp and a friend was calling you but you had have force-quit the app, you wouldn't receive calls. For more see here.
  • Location updates either through use of region monitoring or the significant-change location service. See this answer and make sure to read this entire page from Apple docs.

  • Rebooting the device would also undo anything blocked through force-quit

Honey
  • 24,125
  • 14
  • 123
  • 212
1

Reading the Apple documentation here I found this text snippet which should explain your question:

The techniques offered by iOS fall into three categories:

    Apps that start a short task in the foreground can ask for time to finish that task when the app moves to the background.

    **Apps that initiate downloads in the foreground can hand off management of those downloads to the system, thereby allowing the app to be suspended or terminated while the download continues.**

    Apps that need to run in the background to support specific types of tasks can declare their support for one or more background execution modes.

The second option is exactly about downloading the data, which can be delegated to the system even if the can be terminated.

Oleg Danu
  • 4,010
  • 4
  • 26
  • 45
  • Hah! Your answer is the exact opposite of mine, possibly right. I guess we have to all test and give a hands-on answer... – Honey Jul 05 '17 at 14:34