How to run a NSURLSession in ios in background mode - ios

I was trying to open a URL session for a chunked response, I am able to achieve this by setting backgroundSessionConfigurationWithIdentifier in NSURLSessionConfiguration object. The URL session still runs if app goes to background, but the session terminates once I quit the app by swapping out from multitask view. Is there a way to restrict quitting the app till didFinishDownloadingToURL delegate called.
I have achieved the similar functionality in my android app using a native thread (boost thread reside in a .so called by UI thru JNI) which does not terminate if the UI is swapped out from the multi task view. Is there a way to achieve same in IOS app?
regards,
Birajendu

According to NSURLSession guide background session task executes in different process (not thread). And finished even if initiator app was killed. You can reassign to bg task when relaunch app.
In both iOS and OS X, when the user relaunches your app, your app should immediately create background configuration objects with the same identifiers as any sessions that had outstanding tasks when your app was last running, then create a session for each of those configuration objects. These new sessions are similarly automatically reassociated with ongoing background activity.
Not sure is it possible to get the task result if your app was terminate during bg session and session completed before you restart app.

Related

Will download resume after closing my app in Background Mode

I figured out about it is possible to download in background mode of application. I have implemented Background Fetching Mode in XCode and registered background task and its working fine.
Is it possible to resume downloading task after force closing my application by user? How?
No, you can't continue download when your app get terminated by user! Your app must require to remains in background state!!! Because if user force close app that means, he doesn't want to run it anymore. If your app is suspended by system then it can be wake up but not if it's terminated by user!
If an iOS app is terminated by the system and relaunched, the app can use the same identifier to create a new configuration object and session and retrieve the status of transfers that were in progress at the time of termination. This behavior applies only for normal termination of the app by the system. If the user terminates the app from the multitasking screen, the system cancels all of the session’s background transfers. In addition, the system does not automatically relaunch apps that were force quit by the user. The user must explicitly relaunch the app before transfers can begin again.
Update : (As asked in comment)
Refer the apple documentation, It states,
This method lets your app know that it is about to be terminated and purged from memory entirely. You should use this method to perform any final clean-up tasks for your app, such as freeing shared resources, saving user data, and invalidating timers. Your implementation of this method has approximately five seconds to perform any tasks and return. If the method does not return before time expires, the system may kill the process altogether.
For apps that do not support background execution or are linked
against iOS 3.x or earlier, this method is always called when the user
quits the app. For apps that support background execution, this method
is generally not called when the user quits the app because the app
simply moves to the background in that case. However, this method may
be called in situations where the app is running in the background
(not suspended) and the system needs to terminate it for some reason.
After calling this method, the app also posts a
UIApplicationWillTerminate notification to give interested objects a
chance to respond to the transition.
When any task completes, the NSURLSession object calls the delegate’s URLSession:task:didCompleteWithError: method with either an error object or nil (if the task completed successfully).
If the task failed, most apps should retry the request until either the user cancels the download or the server returns an error indicating that the request will never succeed. Your app should not retry immediately, however. Instead, it should use reachability APIs to determine whether the server is reachable, and should make a new request only when it receives a notification that reachability has changed.
If the download task can be resumed, the NSError object’s userInfo dictionary contains a value for the NSURLSessionDownloadTaskResumeData key. Your app should pass this value to call downloadTaskWithResumeData: or downloadTaskWithResumeData:completionHandler: to create a new download task that continues the existing download.
If the task cannot be resumed, your app should create a new download task and restart the transaction from the beginning.
checkout here: Life cycle of URL Session
Yes—if I understood your need right—Apple allows this with State Preservation and Restoration APIs:
Return your app to its previous state after it is terminated by the system.
Check Apple's article: Preserving Your App's UI Across Launches, for an overview of this framework.
Details about preservation process can be found in article: About the UI Preservation Process
Details about restoration process can be found here: About the UI Restoration Process
Raywenderlich have—a little outdated—tutorial implementation of this framework # State Restoration Tutorial: Getting Started

NSURLsession background transfer service for download task, can it work even when App is suspended?

Before asking a question, let me clarify what I understand about iOS App states:
Backgrounded: In this state, your app is not in the foreground anymore but it is still able to run code.
Suspended: Your app enters this state when it’s no longer able to run code.
Now, I wanted to keep downloading going on when App is in background, i.e. it still does exist in multitasking screen. It is working as expected with background transfer service.
But, in some tutorial reference, I have read that you can perform downloading even when App is Backgrounded / Suspended. Can it work even when my App is suspended, i.e. removed from multitasking screen ?
I have been reading many documents including Apple class reference regarding background transfer service with download task, but no one clarifies that the download will not work when App is suspended (killed).
Appreciate your thoughts and advices !!!
If your app has been suspended by the system (without force quiting from multitasking screen) your background session will continue to work.
If you force quit the application all download tasks will be canceled.
The following is from backgroundSessionConfigurationWithIdentifier(_:) documentation :
If an iOS app is terminated by the system and relaunched, the app can use the same identifier to create a new configuration object and session and retrieve the status of transfers that were in progress at the time of termination. This behavior applies only for normal termination of the app by the system. If the user terminates the app from the multitasking screen, the system cancels all of the session’s background transfers. In addition, the system does not automatically relaunch apps that were force quit by the user. The user must explicitly relaunch the app before transfers can begin again.
Apps displayed in the multitasking UI aren’t necessarily executing code or fetching data. Listed apps may be suspended or not running at all
[[UIApplication sharedApplication] applicationState] will check your application state, you can test your app.
NSURLSession class can hand off downloads and uploads to the operating system when the app becomes inactive. As with almost all background execution APIs, if the user force quits from the multitasking UI, the background operation will terminate
In iOS 7, Apple added support for background fetch—a kind of smart, per-app crontab that wakes up at opportunistic times. There is no way to force background fetches to execute at exact intervals. iOS checks how much data and battery power was used during previous background fetches when scheduling future callbacks.
Background fetches can also be triggered by remote push notification and have a very similar delegate method with the same completion handler.
Full Tutorial is here
https://blog.newrelic.com/2016/01/13/ios9-background-execution/

iOS Background Execution

I am trying to understand Apple's doc for Background Execution:
Once configured, your NSURLSession object seamlessly hands off upload
and download tasks to the system at appropriate times. If tasks finish
while your app is still running (either in the foreground or the
background), the session object notifies its delegate in the usual
way. If tasks have not yet finished and the system terminates your
app, the system automatically continues managing the tasks in the
background. If the user terminates your app, the system cancels any
pending tasks.
When all of the tasks associated with a background session are
complete, the system relaunches a terminated app (assuming that the
sessionSendsLaunchEvents property was set to YES and that the user did
not force quit the app) and calls the app delegate’s
application:handleEventsForBackgroundURLSession:completionHandler:
method. (The system may also relaunch the app to handle authentication
challenges or other task-related events that require your app’s
attention.) In your implementation of that delegate method, use the
provided identifier to create a new NSURLSessionConfiguration and
NSURLSession object with the same configuration as before. The system
reconnects your new session object to the previous tasks and reports
their status to the session object’s delegate.
If I use NSURLSession, so when app goes background when uploading process is still on going, the process won't be killed or died as long as the application isn't terminated by user (I assume this is by killing my app from app list) ?
Read the text carefully. As all good documentation, it says very clearly what it means, and you just need to read it carefully.
You didn't read it carefully.
There are three cases: Your app is still running when a task finishes, your app has been shut down by the system when the last task finishes, or the user has closed down the app before the last task finishes. No, it doesn't say anywhere that the app is kept alive. And the documentation says clearly what happens in each case.
iOS kills apps that are in the background and makes it look to the user as if they are still running.

NSURLSession resume from crash

Has anyone got any tips on how to reconnect to a download in a NSURLSession after a crash.
If my app crashes, I believe the download continues on the device anyway, and then when my app relaunches I can use the same sessionID in:
NSURLSessionConfiguration.backgroundSessionConfiguration(sessionID)
However, am I then supposed to call 'session.getTasksWithCompletionHandler' to see if there are any tasks? The docs aren't clear.
My UI has a download progress bar for each file download, so ideally when I relaunch the app, it would try and reconnect and hook the progress back up to the UI.
At the moment what is happening is I restart the download, and my progress bar flickers because there are 2 downloads in progress - the old one and a new one, and then it gets itself into a state...
Ok here is what I found, in the URL System Programming Guide:
In both iOS and OS X, when the user relaunches your app, your app
should immediately create background configuration objects with the
same identifiers as any sessions that had outstanding tasks when your
app was last running, then create a session for each of those
configuration objects. These new sessions are similarly automatically
reassociated with ongoing background activity.
So once I create the session again and hook up the delegate, the delegate methods start firing again, and I use getTasksWithCompletionHandler to see if there were any tasks in progress and return this to my UI and the rest of the system

NSURLSession background download - resume over network failure

After reading the Apple documentation about the background download with the new iOS7 api (NSURLSession), I'm a bit disappointed. I was sure that Apple was managing the pause/resume over the network availability in the background (or provide an option to do so) but no…
So reading the documentation, this is what we've got:
https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/URLLoadingSystem/NSURLSessionConcepts/NSURLSessionConcepts.html
When any task completes, the NSURLSession object calls the
delegate’s URLSession:task:didCompleteWithError: method with either an
error object, or nil if the task completed successfully.
If the task is a resumable download task, the NSError object’s userInfo dictionary
contains a value for the NSURLSessionDownloadTaskResumeData key. Your
app should use reachability APIs to determine when to retry, and
should then call downloadTaskWithResumeData: or
downloadTaskWithResumeData:completionHandler: to create a new download
task to continue that download. Go to step 3 (creating and resuming
task objects).
So far I understand the solution, but my question is: What architecture is the best to handle the loss of the network and resume downloading in the background?
On my side I'm using reachability and each time the network is available, I resume all tasks (referenced over a NSArray when creating), and suspends them when network is lost. This works well in foreground but for the background I need help on the following points:
If my app has no connectivity in foreground, if I go to the background without connectivity all my tasks remains suspended and won't came back if network is available…
Losing network in background, stop all my downloads/tasks.
Scenario:
In foreground, I start downloading my tasks
I go to background and after 10s switch to "aireplan mode"
All my tasks got an error. So in the method URLSession:task:didCompleteWithError: I resume them using
downloadTaskWithResumeData or if I can't (because some have not
enough resume data) I'm creating a new task without resume-ing it (except if network is back at that time).
Then I put the wifi up
As I'm still in background I cannot trigger a "resume" when network is back without launching the application…
How do I address these points? Have I missed something?
As I'm still in background I cannot trigger a "resume" when network is back without launching the application…
you can use "background fetch",when the app is launched by fetch,then you can check network and resume the download task.
You should create the NSURLSession with background configurations, then your task is sent to a background demon and your app get called when it is completed.
Implementing:
application:handleEventsForBackgroundURLSession:completionHandler:
in the app delegate - without calling the completionHandler - causes the app to hang around in the background after the device loses its connection whilst suspended. That way, the app can still listen to reachability notifications and restart the download when a network connection becomes available once again. However, this is a pretty dodgy approach and may not pass Apple's app store submission guidelines. Additionally, this approach isn't much help when the connection is lost while the app is in the foreground and the connection regained whilst the app is suspended.
In the end I did the following:
Made use of the application:handleEventsForBackgroundURLSession:completionHandler: notification to pause my downloads in the background.
Made use of the intermittent background fetch notification (ie. application:performFetchWithCompletionHandler:completionHandler) to check connection status and restart any paused downloads. (hat-tip #gugupluto)
This still doesn't provide optimal download performance and may lead users to wonder why their "background download" hasn't finished once they reopen the app, but it seems to be the best we can hope for from Apple for now.

Resources