AVAggregateAssetDownloadTask stops sending updates when going to background (perhaps stops downloading) - ios

First off, I'm on iOS 13.6.1
I'm downloading HLS videos to later play offline. It all works as expected when in foreground, all the logic does what it should and there doesn't seem to be any problem.
But when the app goes to background I stop getting updates for the AVAggregateAssetDownloadTask (I mean, I don't see the logs in the delegate callbacks printing in the Xcode console).
I followed the documentation:
NSURLSessionConfiguration sessionSendsLaunchEvents is set to true
The AppDelegate implements handleEventsForBackgroundURLSession
The Session delegate implements URLSessionDidFinishEventsForBackgroundURLSession
Also, I have background modes enabled for fetch.
The crazy thing is, if I hook up the instruments network monitor, I do get all the updates. And I see that the download keeps going in the background.
Even without the changes mentioned in the documentation indicated above, I still see that the download keeps going in the background with the monitor open.
But it seems that the network monitor keeps the app alive somehow, because if I don't open it, then I get zero updates.
To be clear, I'm not even thinking about downloads starting or finishing in the background, and waking the app or anything of that. I just want to start a download, go to the background and have the download keep downloading stuff and notifying me (which AFAIK is done in a separate process, and I can confirm from the network monitor it is).
An example:
open the app
start a download
go to background
no updates are shown
open the instruments network monitor and connect to the app running in the device
suddenly start getting updates
I'm running out of ideas. Nothing makes sense anymore, short of a bug in the framework.
Any sort of idea will be greatly appreciated, no matter how simple it may appear.
Thanks in advance!
PS: when going to settings>storage I sometimes see that the file size grows while in the background, which would mean that the download is continuing even if I don't see updates from the delegate callbacks. At some points I saw the file size unchanged over a period of background time, but that might have been an unrelated thing (I hope).

Related

Monitor app and lock state changes in the background on iOS

I’m trying to make a focus timer app like Forest, that detects if you leave the app, either by going to the home screen, switching to another app or by first locking the phone and later proceeding to a different app through notifications, widgets, camera etc..
What I can’t figure out is how to monitor such state changes even after the phone has been locked for a while. There are no “background modes” covering this use case, so I would expect the app to get suspended after a while in the background. Nevertheless, apps like Forest do this successfully. Do I need a workaround to keep the app awake, or am I missing some approach that doesn’t require background execution at all?
Help much appreciated!
If I had to guess, it's probably a combination of a few things:
Use the normal app delegate callbacks for app state transitions to determine if the app is still able to execute code. That's the easy part.
You could do something with string and regex processing of console logs to detect when other apps open, close, etc. and other activity which would allow you to detect activity on the device coming from places other than your app.
You need to use some sort of framework that Apple says can get data even in background state. One such framework is Core Motion. There may be others as well that suit your app's specific needs better.
See Execution States for Apps.
See Background Execution.
See Cocoanetics: Accessing the iOS System Log.
See Keep iOS App Awake To Monitor Movement.
See Apple System Log Facility.

Fetch freezes after react-native app comes back from suspended state on IOS

I'm developing a react-native app, and we noticed something strange. When the app comes back from suspended state (it doesn't happen from normal background state I think), often, the fetch freezes afterwards.
To trigger it, put on battery saver (I think it will make the app go to suspended state a lot quicker), then put the app in the background, do some other stuff on your phone for a minute or 5-10, open the app again and use it.
In our case we navigated to an other screen which tried to fetch three lists of objects at the same time from the backend. In the backend, we noticed only one call coming through, and the Promise.all(...) on the three calls never gets resolved or catched. So there is no error either. Afterwards all calls work again and the problem seems to be gone. So it's a one time issue.
This never happens when not coming from suspended state.
This is extremely hard to debug, since when the app goes to suspended state, the debugger is disconnected.
We haven't tested this yet on Android, it could be that the problem exists there as well.
My gut feeling tells me, it has something to so with the internal networking of IOS or the fetch library of RN. (e.g. when reconnecting to wifi, coming from 4G or something)
Has someone experienced the same problem already or has more insights on why this is happening or how to solve this? (e.g. use a time-out and retry mechanism, which I would like to prevent, force a api call on state change to active - although react-native can't detect the suspended state)
Thanks in advance!
EDIT:
Forgot to add: we're using react-native 54, but are in the process of updating to 57. I'll report back if the issue is still happening on 57...
EDIT 2:
Atm, we solved it by doing to the calls after each other instead of the at the same time (so fetch().then(fetch().then(fetch())) which seems to resolve the issue. So yet again, doing the calls at the same time, never fails normally, only after a suspended state of the app.

Keep an app active in the background?

I have a volume control app in the iOS store, but one problem that my users have frequently is that the device kicks it for memory control. Is there any way to either force it to stay active (by permission) or to at least alert the user when its no longer active or in danger?
All the answers are under Background Execution in the iOS Developer Library.
Of course, here's the philosophy:
Always try to avoid doing any background work unless doing so improves
the overall user experience.
See Table 3-1 for the types of background execution. There's a category for audio but it requires audio to be played from the app. It does not sound like your app fits in here. So you'll want to look at notifying the user.
You could send a notification to the user when the app falls to background with applicationDidEnterBackground(), or just before it quits with applicationWillTerminate(), supposedly time-permitting.
Running in the background is permitted. I have an app that does it and while the rules have changed and adapting to it has been gut wrenching at times, it is pretty straightforward. In your case, I would think that you could setup to be notified and then just disappear, no? waking from a notification is part of the state changing protocol and you should be able to do that.
As a point of comparison, in Android, background operations like yours would be implemented as services and would have no fear of being terminated. The strangest thing about the way ios chooses to allow background activity is that you are applying for status one time. In Android, I was able to indicate that a single activity should be kept alive when the app is backgrounded, all others could be terminated. There is no way to do that in ios.
I take it my disappear and wait for notifications is probably not going to work for you because there is no way to be notified of volume changes. You must be polling? In which case, you probably are going to have to ask to be kept alive. Would be nice if you could just piggyback on other services, e.g. keep me alive while music is playing.
To answer your other question, yes you will get notified if/when you really are going to get termed, so you could send a notification at that point.

performFetchWithCompletionHandler never gets fired

1) My plist configuration to provide backgroundmode:
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
</array>
2) In didFinishLaunchingWithOptions I have:
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:1.0];
3) I declared the protocol UIApplicationDelegate in the delegate.
4) I implemented the following method, but it never gets fired. (It only works if I simulate the fetch with "XCode->Debug->Simulate Background Fetch".)
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
Why? Is this a DP5 beta error? Should I radar this?
Running your app in the iOS Simulator, in Xcode Debug mode, you can force a background fetch from the Xcode menu:
Debug > Simulate Background Fetch
May work for a tethered device, I haven't tried it recently.
I'm afraid this is hard to debug on a device because you're not guaranteed it is called in the amount of time you specify.
setMinimumBackgroundFetchInterval means that it is not called in an interval which is smaller than the value you specified. But there's no setMaximumBackgroundFetchInterval.
So if iOS decides to call your app just once a day or even just once a week than it won't be called more often regardless your minimumBackgroundFetchInterval. AFAIK iOS decides when to call performFetchWithCompletionHandler measured by the pattern when and how often the users start the app.
There are many considerations:
Make sure the background fetch capability was set in the plist.
Make sure the background fetch capability hasn't been disabled for this particular app, or in general, in the device's Settings app.
Make sure to set the minimum fetch interval.
Make sure you gracefully leave the app (e.g. just hit the home button and fire up another app and/or just lock the device). But if you kill the app (by “force quitting” by double tapping on the home button and swiping up or, for those devices without home button, swiping up from the bottom to pull up the task manager and then swiping up on the app in question) that will prevent the OS from offering your app a chance to fire off subsequent background fetch requests (at least until the user runs the app again).
Make sure you are testing this on physical device and not running the app via the Xcode debugger. Being attached to the debugger changes the behavior of background operations.
Make sure the app is actually doing some network requests. If you have app that performs no network requests at all, it won't participate in background fetch. If you do, for example, a little test app with "background fetch" and don't issue any network requests, you won't participate in background fetch.
Likewise, if the OS starts up your app in background mode so it can perform a background fetch, if you don't actually perform a network request, the OS may stop offering your app the ability to perform background fetches in the future.
Make sure to call the completion handler, and do so within the allotted time, or your app may not participate in background fetch in the future.
The timing of when the OS performs background fetch is dictated by poorly documented rules that may change in the future. But the relevant factors include:
Whether the device is connected to power and/or is sufficiently charged;
Whether connected to WiFi or not;
How often the user actually fires up the app;
Whether the device is doing other network related tasks (e.g. whether background fetch can be coalesced with other network operations);
How frequently past background fetch requests resulted in there being data available.
In my experience, after the app is run the first time, if connected to wifi and power, if you wake the device about 5 minutes later, the app will perform background fetch. This isn't a hard and fast rule, but just what we've experienced in the past.
But many new developers post on Stack Overflow with questions like “how can I have app request data ever x minutes (or hours)”, “how can I request data every day at 2 am time”, etc. The short answer is that you can't. The OS decides the timing of background at its own discretion. You cannot control this (other than the minimum request interval; but you cannot control the maximum interval, as the OS controls that).
This may seem obvious to many, but make sure you've got a reliable way of knowing whether background fetch process is running correctly or not. User Notifications framework can be used to present some alert so you know if the background request resulted in something. Alternatively, os_log or Logger “Unified Logging” (see WWDC 2016 Unified Logging and Activity Tracing or 2020’s Explore logging in Swift) can be used to post messages on device that can be monitored on macOS Console app. But more than once, I've seen users do something like waiting for message to show up in Xcode or waiting for UIAlertController. You need some mechanism that works when not connected to Xcode and when the app never enters foreground.
Using your device you can fire application:performFetchWithCompletionHandler with the following steps:
Put your app in the Background state
Lock your device and wait 5 minutes.
Unlock your device, this will fire the method
(It only works if I simulate the fetch with "Xcode->Debug->Simulate
Background Fetch".)
It's because you're in Debug mode. Please try launch app without Xcode.
Another thing to check is your plist file. Make sure the UIApplicationExitsOnSuspend key is not present.
Many people here on Stack Overflow have recommended using that setting as a way to force your app to start fresh each time it's launched. That does work, but the side effect is that it prevents the new iOS 7 background fetch feature from being triggered.
If application: performFetchWithCompletionHandler: never gets fired (unless you simulate it using Xcode), check also if "Background App Refresh" preference is "On" for your app. (Settings app -> General -> Background App Refresh)
Also, background fetch is disabled if the iPhone is in Low Power Mode.
Apple provides an algorithm which defines how often the background fetch should trigger, based on your own usage of the app. If you use it a lot, then it will fetch as often as possible, but if you use like at 4pm every day, the background fetch should trigger just before, so your data is updated when you launch it.

Is there a way to prevent iOS6 to kill the specific task process?

I know that iOS has its own task management method and users may never need to care about the processes background. But my requirement is to ALWAYS keep a program alive, it cannot be killed under any circumstance.
Is there a way to do this like "LOCK" or something else function already existed? I'm using iPad4 ios6.01 system.
Thanks
No, that's not possible. (Nor should it be if you think about it. What's to stop every app from saying that it can't be killed?)
The closest you can get is for things like VoIP apps that do run in the background and automatically respawn when they die.
The alternative is to work like every other app: when your app goes into the background you save state so you can restore if it gets killed. iOS 6 even has the state restoration functions.
Short and simple
No, there is no way to make your App "unkillable".
A bit longer
If your App requires to receive location updates while it is running in background, you can use the standard way Apple offers to do so.
Two other options are: your App is an VoIP application (afaik they also get autostarted on system boot) or you're playing audio in the background. Without knowing too much details about how iOS handles such Apps, they might get killed if the iDevice runs out of memory (or the user kills it). But you probably already knew that.
However, as you already mentioned, iOS manages everything on it's own and will kill Apps that run in background to free memory. Additionally as we all know, a user might kill your App at any time using the task switcher of iOS.
And don't try to use the described methods just to run tasks in the background. If Apple finds out about it, your App will be rejected/removed from the App Store quickly.

Resources