Resuming a file download in the background on iOS sometimes corrupts the file

To download files, we have two NSURLSession objects. One configured for foreground downloads and one for background downloads. Initially, we download user-requested files in the foreground using downloadTaskWithRequest, because foreground downloads are faster than background downloads. We then also start a background task for each download using beginBackgroundTaskWithName:expirationHandler:. This background task's expiration handler starts a background download if the download didn't complete in the foreground. It cancels the foreground download with resume data using cancelByProducingResumeData. A background download task is then started using downloadTaskWithResumeData.

Now, testing has shown that background download tasks resume correctly (the urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:) callback is called by iOS) if the background task is started immediately. However, iOS can decide to start these background download tasks later. As a sidenote, the isDiscretionary property of our background download session is set to the default, which should be false.

The issue we have is that background downloads that are resumed several minutes (6,5 minutes in the session I'm currently looking at) after their original foreground download was cancelled, are not resumed properly. They download the remaining part of the file, but the first part that was downloaded in the foreground is lost. What could have happened? Perhaps the temporary file has been deleted in the meantime? Is there some way to maintain this file or detect this case?

Post not yet marked as solved Up vote post of p_b_omta Down vote post of p_b_omta
873 views

Replies

Check out the background tasks section in Networking and Multitasking - TN2277.

That's an interesting document, as it gives some details on how iOS handles apps in the background. Thanks for sharing. However, it doesn't help me as I'm using a newer API. NSURLSession configured with backgroundSessionConfigurationWithIdentifier. This is a newer API than mentioned in that document. We are following the Download Files in the Background document.

However, I cannot find documentation about what happens when iOS decides to resume a background download later.

Filed a bug for this: FB12131501.

I cannot find documentation about what happens when iOS decides to resume a background download later.

Last time I check — and this was a very long time ago, circa 2014 — CFNetwork attempts to resume a download if:

  • The request’s scheme is http or https

  • The request’s method is GET

  • The response is an NSHTTPURLResponse [1]

  • The response includes either the ETag or Last-Modified headers, or both

  • The file to which the task is downloading is present [2]

And for the download to actually resume, the server must support byte-range requests.

The most common cause of corruption problem is the server failing to support byte-range requests properly.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Which should always be the case given the first point.

[2] This file is managed internally by NSURLSession, so you shouldn't trip over this case (unless there's been a cache folder cleanup in the interim).

  • 1 one for the resumable file download behavior here ^
Add a Comment

Thanks Quinn, that look similar to what the documentation says, so it seems to apply nowadays too.

The bit you mention about the temporary file being managed internally by NSURLSession is interesting in our case. We use one NSURLSession for downloads when our app is in the foreground, but resume with background downloads on a second, differently configured NSURLSession when the app is suspended.

From this, it seems that we are running into the case where the temp file is cleaned up, since we cancel the download (with resume data) on the original session.

Through a feedback I filed, I understood that the background downloader has been rewritten for iOS 17. We have retested this for over a week with iOS 17.3 and the issue has not appeared again. Very nice.

Add a Comment