BackgroundTaskWithExpirationHandler: thread too short?

When our iOS app authenticates with the server, a background thread is initialized using the

beginBackgroundTaskWithExpirationHandler: interface.

The intention is that when the user backgrounds our application while in an authenticated state, the

background task will end the authentication session with the server after 3 minutes of not returning to the foreground.



This design has been working well for us for years. Until the iPhoneX arrived and a small handful of clients started to

complain that their session was being dropped after shortly sending our app to the background and returning to the app within a

short time. Which means that our expirationHandler is being called after an unexpectedly short amount of time (usually 15 seconds).



when entering the background, our task calls

[UIApplication sharedApplication].backgroundTimeRemaining

which we typically expect to return something like 170.

on the iPhoneX it returns 1.797693134862316e+308



Why is the expirationHandler being called after 15 seconds on iPhoneX hardware?

How can I allow for more time in the background before the expirationHandler is called?


Thanks,


|K<

Accepted Reply

What i am missing from your console log above is a log entry when the app is sent to the background, so although your task ran for 30 seconds it is unclear how much of that time was spent in the background.

Pretty much all of it. I combined “tapped the button” and “pressed Home” in step 5 in an attempt to make that clear. Feel free to try this for yourself, adding a log point to your

-applicationDidEnterBackground
method to confirm this behaviour.

does the main thread checker throw a false positive when calling

backgroundTimeRemaining
?

The general rule for UIKit is that the entire framework is main thread only unless otherwise documented. The docs for

backgroundTimeRemaining
don’t mention thread safety, so I think it’s better to err on the side of caution and only call it from the main thread.

Having said that, my preferred option is to not call it at all (-:

Also, how does an app like Music.app play music in the background?

iOS has tight restrictions on background execution. Some facilities, like

UIApplication
background tasks, are available to all apps. Others require that your app declare support for a specific background mode. For example, a third-party music player app [1] would use the
audio
background mode so that it can run indefinitely after moving to the background in order to continue playing music.

IMPORTANT App Review is particularly strict about use of these background modes. To quote clause 2.5.4 of the App Store Review Guidelines:

Multitasking apps may only use background services for their intended purposes: VoIP, audio playback, location, task completion, local notifications, etc. If your app uses location background mode, include a reminder that doing so may dramatically decrease battery life.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] I’m deliberately not addressing the Music app because it’s built in to the OS and thus doesn’t have to follow the same rules as third-party apps.

Replies

Why is the expirationHandler being called after 15 seconds on iPhoneX hardware?

I very much doubt this is correlated to the iPhone X hardware, but rather to the iOS version they’re running. Last I checked (iOS 13.0b2), iOS 13 had dropped the from-the-foreground background task time to 30 seconds (see my UIApplication Background Task Notes).

Anyway, my advice right now is to reanalyse the reports you’ve received to see if they’re correlated to a specific iOS version.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi Quinn,


Thank you very much for your response.

I too was very suspicious of the hardware correlation of this problem (reported to us by our clients and verified by us).

We could however verify that on iOS 11 (which is what the iPhoneX shipped on, no?) the behaviour for the iPhoneX was different than on every other hardware. And yes: the behaviour is different when the device is connected to a usb power supply. The discrepency between the iPhoneX and the older devices remained through iOS12 an is still present in iOS13. According to DTS, the beginBackgroundTaskWithExpirationHandler: API is not suitable for our use case. At this point it is also unclear on what API would be appropriate for this. Specifically i am trying to fulfil the following requirement:


When the iOS application is sent to the background, a thread needs to survive for up to 180 seconds which can perform cleanup duties including close server sessions, clean disk caches of sensitive data, and clear cookies.


Your advice is much appreciated,


|K<

Kent Clelland

Avaloq Evolution

Zürich, Switzerland

iPhone X and later? Or just iPhone X?

I don’t have easy access to an iPhone X to test on right now, but I do have an iPhone 11 (running iOS 13.3). It exhibits the standard (on iOS 13 and later) 30 second background task time. Specifically:

  1. I wrote a small test project with this code wired up to a button.

    NSLog("QQQ task will begin")
    var task = UIBackgroundTaskIdentifier.invalid
    task = UIApplication.shared.beginBackgroundTask {
        NSLog("QQQ task did expire")
        UIApplication.shared.endBackgroundTask(task)
    }
    NSLog("QQQ task did begin")

    .

  2. I ran it on the device from Xcode, just to install it.

  3. I stopped it.

  4. I ran it from the home screen.

  5. I tapped the button and then pressed Home (well, swiped up).

  6. I monitored the device’s system log using Console on the Mac.

Here’s what I saw:

… 23:14:06.753939 +0000 … QQQ task will begin
… 23:14:06.754779 +0000 … QQQ task did begin
… 23:14:34.739034 +0000 … QQQ task did expire

As you can see, the background task runs for (roughly) 30 seconds before it expires.

I repeated this test on my iPhone 6s Plus (running iOS 13.1.3) and saw the same results:

… 23:15:46.053642 +0000 … QQQ task will begin
… 23:15:46.054297 +0000 … QQQ task did begin
… 23:16:14.063030 +0000 … QQQ task did expire

Earlier you wrote:

when entering the background, our task calls

[UIApplication sharedApplication].backgroundTimeRemaining
which we typically expect to return something like 170. on the iPhoneX it returns 1.797693134862316e+308

That value is

DBL_MAX
, which is the value you get back when there’s no limit to the background task time available. You typically see this when your app is in the foreground.

In general you shouldn’t pay to much heed to

backgroundTimeRemaining
, as I explained in UIApplication Background Task Notes. If you’re basing your analysis on
backgroundTimeRemaining
, you should go back and retest as I’ve shown above. In networking, the only way to know whether you can connect to a service is to try the connect; with background tasks, the only way to know how much time you’ll get is to actually use it.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi Quinn,

Thanks for your continued support,

What i am missing from your console log above is a log entry when the app is sent to the background, so although your task ran for 30 seconds it is unclear how much of that time was spent in the background.


also I wonder: does the main thread checker throw a false positive when calling backgroundTimeRemaining?

Main Thread Checker: UI API called on a background thread: -[UIApplication backgroundTimeRemaining]

PID: 1413, TID: 1709021, Thread name: (none), Queue name: com.apple.root.default-qos, QoS: 0


when calling backgroundTimeRemaining from the foreground it returns DBL_MAX, when calling it in the background the Main Thread Checker kicks in. So, is this a false positive then?


Also, how does an app like Music.app play music in the background? Can I use the same mechanism to achieve my goals as well?

Thanks,


|K<

What i am missing from your console log above is a log entry when the app is sent to the background, so although your task ran for 30 seconds it is unclear how much of that time was spent in the background.

Pretty much all of it. I combined “tapped the button” and “pressed Home” in step 5 in an attempt to make that clear. Feel free to try this for yourself, adding a log point to your

-applicationDidEnterBackground
method to confirm this behaviour.

does the main thread checker throw a false positive when calling

backgroundTimeRemaining
?

The general rule for UIKit is that the entire framework is main thread only unless otherwise documented. The docs for

backgroundTimeRemaining
don’t mention thread safety, so I think it’s better to err on the side of caution and only call it from the main thread.

Having said that, my preferred option is to not call it at all (-:

Also, how does an app like Music.app play music in the background?

iOS has tight restrictions on background execution. Some facilities, like

UIApplication
background tasks, are available to all apps. Others require that your app declare support for a specific background mode. For example, a third-party music player app [1] would use the
audio
background mode so that it can run indefinitely after moving to the background in order to continue playing music.

IMPORTANT App Review is particularly strict about use of these background modes. To quote clause 2.5.4 of the App Store Review Guidelines:

Multitasking apps may only use background services for their intended purposes: VoIP, audio playback, location, task completion, local notifications, etc. If your app uses location background mode, include a reminder that doing so may dramatically decrease battery life.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] I’m deliberately not addressing the Music app because it’s built in to the OS and thus doesn’t have to follow the same rules as third-party apps.