Demystifying Background Uploads/Downloads in iOS

Jai Chaudhry
The Startup
Published in
5 min readSep 17, 2020

--

What’s the general thought that comes to any iOS Developer’s mind when they think of background tasks?

That’s right, you would immediately think of the above API in UIApplication to extend the time of your app to run on background in order to complete your uploads/downloads. But what if the download/upload size if huge, let’s say for 500MB for example, and you are on LTE/3G connection. Would this still work? The answer is that there is no guarantee that your upload/download would complete, it is highly probable that your app might be killed by the system due to various reasons(like memory pressure, battery etc.) in between.

How do we guarantee that this works? Does iOS have a solution for the same?

Photo by Jake Young on Unsplash

The answer is YES, NSURLSessionConfiguration provides APIs to help with uploads/download working smoothly when backgrounding the app. The uploads/downloads using this API will work even if your app is killed by the OS. They won’t work if you force kill the app. iOS basically passes the upload/download task to the system’s upload daemon thread if your app is killed and for this very reason, its important to save your file to a permanent location on device.

In the above code snippet, we need to make sure the identifier passed to the background session configuration is a unique identifier.

Q: Now, how do we get the status of a running upload/download?

A: You can add the following API in your AppDelegate, which provides you with an identifier for a particular NSURLSession. OS usually wakes up your app with the once all the tasks in your session are complete and calls the following the API to let you know about the session progress.

You can use the above method to reconnect your NSURLSessions and get the progress of your uploads/downloads. You need to make sure that the identifier you pass here is actually the identifier you are looking to get the progress for, otherwise things might not work as expected.

Q: How do I create a task for NSURLSession with background configuration?

A: We can simply use a NSURLSessionUploadTask/NSURLSessionDownloadTask to upload/download a file along with the background configuration created above.

What are the major problems faced using this pipeline?

  • You need to save any file that you want to download/upload onto the disk. This generally also increases the IO operations during the time of upload/download, you will see a significant increase in CPU usage while the upload/download is going on.
  • Custom protocols are not supported.
  • Debugging this pipeline is very tough. The only way to see if your downloads/uploads are still running is to check logs on mac console or system diagnosis logs. Check section below.
  • Its tough to mimic the scenario where OS force kills the app and you still want to check if your downloads/uploads are working. On phones below iPhoneX, you can replicate this by holding the power button until you see the switch off screen and then holding the home button. I am not sure if there is a way to do this in iPhoneX and above. Would be great if someone out there knows :)
  • You can’t keep on keep creating unlimited sessions. If your app is not active and is killed by OS, OS would wake up the app after every session finishes the tasks and iOS Resume Rate Limiter comes into picture. Every time a session wakes up the app, the wake up time increases every time and would keep on increasing and might result into your app not waking up at all. The best optimal strategy to avoid this is to just have one session and just keep on adding tasks to the same session. (Note: the above situation only occurs for uploads and not downloads.)
  • Testing in all conditions like wifi, slow wifi, LTE, 3G, no network, resuming uploads etc. takes a huge chunk of QA time. Found this great link for testing tips and tricks.

Debugging Screenshots from Mac console when the app is force killed by OS and the uploads are still continuing

This is how the logs would look like when a background configuration session is initialized and the uploads are about to start.

Background upload starting logs

And this is how the logs would look like when you have backgrounded the app, and OS has killed your app, but the uploads are still going on. The OS will try to wake up your app and send you a app delegate event as soon as all the tasks of a session are finished.

Background upload end logs

Takeaways from our implementation at Fyusion Inc

  • We moved to a single file upload just to make sure its easier to maintain status of an ongoing upload and everything can be done with just one task in a NSURLSession. Highly recommend doing it this way if you have a queueing architecture in your pipeline. Just keep on adding tasks to a single NSURLSession. We learnt this hard way that it was not wise to create multiple NSURLSession’s for this pipeline.
  • Moving to one NSURLSession also takes care of iOS Resume Rate Limiter. (Note: Having a single file also has its own problems, like how would you handle very large size of files. We are still working on it and would probably go ahead with sharding of files with huge file sizes.)
  • Bombarding your app with multiple NSURLSessions might also lead to crashing of the upload daemon thread of your OS and in result crashing the whole device. (But for this the number of sessions need to be crazy high, not a usual scenario :) )
  • Maintaining internal states of background uploads progress is very important especially if you are developing a SDK. The clients need to have access of what state the upload is in even after resuming an app.
  • Resumable downloads is always an option, but resumable uploads is not an option. The only option for optimizing uploads is to create chunks of your file.

--

--