NSURLSession background downloadTasks sometimes calling urlSession(_:downloadTask:didFinishDownloadingTo:) *twice*

I've just implemented background session downloads, and in testing (with 1044 downloadTasks), I'm seeing some strange behavior that's not 100% reproducible. Sometimes when I background the app, when I foreground it (or the OS does), the URLSessionDownloadDelegate's function

urlSession(_:downloadTask:didFinishDownloadingTo:)

gets called twice. I'm also logging the URLSessionTaskDelegate's function

urlSession(_:task:didCompleteWithError:)

and in this case, it does not get called between calls to didFinishDownloadingTo.

Both cases are being called with the exactly same task, session and location. The first call copies the location to a semi-permanent destination (and I confirmed that file is correct), and the second call fails on move because the destination already exists.

I can obviously work around this fairly easily, but wondering if I'm missing something or if there's a bug.

It does appear to happen more reliably when I background for 15 seconds or longer.

A second issue which is reproducible is that while backgrounded, some files are completing downloads and never calling the download delegate's

urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)

I tried resuming one or all of the tasks in applicationDidBecomeActive as suggested in multiple other forums posts, but neither of those seems to resolve the issue. Again, I can work around this (using a combination of totalBytesWritten and the known size of files which have completed downloads), but I'm wondering if I'm missing something obvious.

I actually thought that perhaps the resume() workaround was causing the first issue, but removing it does not have an effect.

wondering if I'm missing something or if there's a bug.

If you see URLSession calling ‘did finish downloading’ twice for the same task, that’s most definitely a bug. I’ve not seen other reports of this, so I recommend that you carefully check your own logic before investing the time to file a bug about this.

A second issue which is reproducible is that while backgrounded, some files are completing downloads and never calling the download delegate's

As far as I know the issues with the ‘did write data’ callback are ongoing (r. 32247561)-:

Share and Enjoy

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

Before filing a bug, I'm trying to test on iOS 14 and 15 to see if this is a regression, or not. On iOS 14, I received on didFinishDownloadingTo callback where the task's response and error were both nil, which I also take to be a bug.

I'm reading in some places that I shouldn't be testing background tasks on the simulator, is this true? I know you've linked to a guide you posted about testing background sessions multiple times in the past, but the link seems broken (probably due to a change in the structure of the forums). Do you have a current link to that guide?

Re: 32247561, is this the bug that's worked around by calling resume() on one of the tasks? If so, that did not help in my case.

I started testing on devices, and on iOS 16 I still see the same double-calling bug, and on iOS 14/15 I still see the same no downloadTask.response bug. If I ignore the absence of a response and keep going, then I do not see the double-calling bug on iOS 14/15, as well as the not reporting all bytes downloaded bug (again, even though I'm resuming the first task).

(EDIT: I do see the double-calling bug, I just seem to have to stay in the background for 25 seconds, longer than on iOS 16)

On a device, the reporting of progress doesn't continue at all after resuming after background, as opposed to on the simulator where it just skips some files (seemingly the ones that completed their downloads while backgrounded, but that's difficult to be sure about).

Disclaimer: iOS 14 testing was on simulator, iOS 15 on device. iOS 16 on both.

I'm reading in some places that I shouldn't be testing background tasks on the simulator, is this true?

Yes.

I know you've linked to a guide you posted about testing background sessions multiple times in the past

The droid you’re looking for is Testing Background Session Code.

probably due to a change in the structure of the forums

Historically, when I linked to a locked thread I would often link to the first post on that thread. Such link did not survive one of our DevForums transitions. For more on this, see this thread.

Share and Enjoy

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

So, the double calling issue might have been happening because I was accidentally recreating URLSession when foregrounding as opposed to when re-launching.

I still can't fix the progress reporting, though. Even with resuming one or all tasks, lots of files just get skipped entirely. I tried using the .progress property of the URLSessionTask and that works well for backgrounding, but the Progress object does not get set up correctly after termination and relaunching, and since it's a read-only property, one can't seem to fix it manually.

the double calling issue might have been happening

Oh, cool. Let me know if you find evidence for any wrongdoing on our part.

I still can't fix the progress reporting

Yeah, that’s not a big surprise. Sorry. I haven’t looked this in a long time — since coming up with the ‘resume’ workaround in fact — so it’s possible that things have declined since then. Unfortunately it’s hard for me to allocate time to dive deeply into such issues in the context of DevForums. If you want to look at this in more depth, open a DTS tech support incident and we can pick things up in that context.

To be clear, there’s no guarantee that there is a workaround; the final resolution might be that this is just broken )-:

Share and Enjoy

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

NSURLSession background downloadTasks sometimes calling urlSession(_:downloadTask:didFinishDownloadingTo:) *twice*
 
 
Q