Re: NSURLSessionDownloadDelegate Methods on iPhone iOS 10.3.1

I've recently been integrating Background Transfer Service into an application so that the user is able to download files in the background.

I did most of my building/testing on an old iPhone 6 device that was running iOS 9.3.5 (I try to keep my previous device back a version of iOS). Background Transfer Service works great on this device. I am able to put 100+ download tasks into the queue and they all finish and report progress as expected after sending the application into the background and then re-opening the application.

This also appears to work great on a new 5th generation iPad running iOS 10.3.

Where things get weird is on my iPhone 7 Plus which is running iOS 10.3.1. The downloads kick off fine, progress is reported as expected in the didWriteData method, however if I background the application and wait ~10 seconds and re-open the application the progress never catches back up to what progress would have been done in the background and the progress never increments at all (it stays where it was when it was backgrounded). The didFinishDownloading method does end up getting called however it appears to wait until the very end of all the download tasks completions for that method to fire for all of the remaining tasks. So the didWriteData, didFinishDownloading, didCompleteWithError, etc. all happen right at the end in one burst.

I have however had a few instances where my iPhone 7 Plus device running iOS 10.3.1 did show progress after bringing the app back into the foreground however those instances I could could on one hand. More often than not (9 times out of 10) the progress is never reported on this device after re-opening the application.

I am at a bit of a loss and am wondering if there is a known bug we should be looking out for, and if so when we could expect a fix? Or if there are currently any known work arounds. From my testing/debugging I cannot get progress to work in my application on the iPhone 7 Plus running 10.3.1. I also had a co-worker test this as well and they also experienced this.

In fact they went a step further and opened the Netflix application on their 10.3.1 iPhone 7 Plus device and began downloading a movie. Progress incremented as expected, they then backgrounded the application and waited ~20 seconds and re-opened the application, only to find the progress get stuck where it was when the app was initially backgrounded and it never moves. After a given amount of time of the progress not moving the download does eventually finish (the progress indicator disappears) and the Netflix movie is downloaded and able to be played. This is the same behavior I see above in my application.

I’m in two minds about this. On the one hand my suspicion is that, if this problem were widespread, I would have seen lots of reports from other developers about it. On the other hand, your investigation so far seems to strongly indicate that this is a bug in NSURLSession.

Three points:

I presume that you’re testing this by running your app from the Home screen rather than from Xcode. See my Testing Background Session Code pinned post for information on why this is important, and for other testing tips.

If this is a problem it’s caused by NSURLSession not correctly reconnecting to the session when you come back from being suspended in the background. One interesting test would be to terminate your app to see if things work in that case.

IMPORTANT Do not test this by force quitting your app. That will cancel all of your requests.

You can test this by adding a ‘Quit’ button to your app that terminates the app by calling exit. This is not acceptable in a production app (see QA1561) but it’s handy for testing.

Have you tried reproducing this with a small test app? The obvious candidate here is the Simple Background Transfer sample code. If the problem occurs in that sample (even if it requires a few tweaks to get it to reproduce), that’s definitely bugworthy.

I'm seeing the same issue with iOS 11. NSURLSessionDownload delegate methods are not invoked when app resumes from background. Could you please let me know, the status of this issue. If not are there any workarounds?

I don’t know of any current problems with the OS that would cause behaviour like that described above. Most issues like this are caused by folks misunderstanding how background sessions are supposed to work. On the main Core OS > Networking page you’ll find a bunch of pinned posts that explain some of the most common gotchas.

Most issues like this are caused by folks misunderstanding how background sessions are supposed to work.

adamzucchi wrote:

I'm not sure how I misunderstood how background sessions work?

That post wasn’t in response to anything you wrote, but in response to ramyad’s post from the 2 Nov.

With regards your issue, I haven’t had a chance to look into this in depth but your analysis so far is strong evidence that this is a bug in the OS.

As to workarounds, I’m curious if you can see progress being made by polling the various NSURLSessionTask properties (like countOfBytesReceived or countOfBytesSent), or via KVO-ing properties on the object returned by progress.

I apologize for digging out an old thread for a problem which may well have been resolved since then, but just to add, for Quinn's benefit:

1. I am seeing it too, on iOS 10.3.3. That is: Home button halts the didWriteData method calls (after a short time which I'll call T), tapping on the app icon does not always resume them. (My "not always" is "about half the time").

2. Yes, to cause the problem it is necessary to wait a few seconds after pressing Home, before tapping the app icon.

3. As has been reported, the didFInishDownloading method is called even if the didWriteData calls aren't coming through.

4. Quinn, I have tried the KVO of countOfBytesReceived, as you suggested. Its behaviour is identical to that of didWriteData. If a KVO is triggered, so is the method call. If no method call appears, no KVO appears.

5. NEW: If, after pressing Home, waiting, and tapping the app icon, the didWriteData calls do not happen, then sometimes pressing the Home button will cause a short burst of didWriteData calls. The time that burst lasts is, to my eyes, about the same as the time T which I mentioned in point 1. As if some sort of "send / don't send" toggle missed a beat and thus goes from NO to YES on pressing Home, only to get shut down shortly afterwards because the app is suspended.

This is not a hugely urgent issue for us, in that users have not reported it. Moreover, the quiescence of this thread suggests that the problem, whatever it was, was corrected in iOS 11 – otherwise I would have expected to see more reports. I have not, myself, attempted to reproduce the problem in iOS 11.

Nevertheless, if anybody sees any sign that the problem does occur in iOS 11 then I will go ahead, attempt to reproduce it, and report any results. It would be good to get this sorted out (if it still needs it).

The download is complete, but the UI is not updated because the delegate is not called.

I had a quick look at (r. 32247561) and I can’t see any indication of movement on that front. I must stress, however, that this is just a progress issue. You don’t need the -URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: delegate callback for learn about requests completing. The correct way to do that is via the -URLSession:task:didCompleteWithError: delegate callback, which, AFAIK, is functioning just fine.

Earlier I suggested two potential workarounds:

Polling the countOfBytesReceived property on the task itself

Getting the NSProgress object from the task and using KVO to observe its various properties (like fractionCompleted)

Various folks have tried the first and confirmed that it’s not working; I’m curious as to whether anyone has tried the second?

I have tried to use KVO instead of polling '''countOfByesReceived''' or '''fractionCompleted''' directly. It also does not work.

Also you might say this is only a progress issue, but I personally think that displaying progress for larger files is essential to having a polished and nice to use app. And downloading large files is exactly the use case, where the user will put the app to the background and periodically check for progress.

Displaying download progress seems like a very basic thing and it is a bit disappointing, that there are no workarounds and no progress from Apples side on this

Your workaround worried me a little because you’re calling resume redundantly, and I wasn’t sure what impact that might have on the session. So I discussed this issue with the NSURLSession engineering team and they suggested that the call to resume was unnecessary, and just calling getAllTasks(completionHandler:) should be sufficient to get delegate events flowing again.

I did some testing of this here in my office and it seems to work, but it’s hard for me to be 100% sure because my standard background session test code was already calling getAllTasks(completionHandler:) as part of its normal operations.

So, my question is: If you remove the call to resume, is your workaround still effective?

Thanks. Your code and detailed instructions highlighted a number of flaws in my understanding of this problem. I’ve since corrected those and now see the behaviour you describe, that is, -getAllTasksWithCompletionHandler: is insufficient but -getAllTasksWithCompletionHandler:and-resume works.

Another interesting thing to note is that this workaround has to be done out of -applicationDidBecomeActive:. I originally tried doing it from -applicationDidEnterBackground: and that didn’t work.

And now I’m off to determine if NSURLSession engineering is prepared to officially endorse this workaround…

And now I’m off to determine if NSURLSession engineering is prepared to officially endorse this workaround…

My persistence paid off! (-:

NSURLSession engineering took a look at this problem and the suggested workaround and they’re OK with folks using it. Their analysis is that you don’t need to call -resume on every task — calling it on just one task should do the trick — but doing so shouldn’t cause problems.

More Like This

Incoming Links

This site contains user submitted content, comments and opinions and is for informational purposes only. Apple disclaims any and all liability for the acts, omissions and conduct of any third parties in connection with or related to your use of the site. All postings and use of the content on this site are subject to the Apple Developer Forums Participation Agreement.