URLSessionConfiguration's httpMaximumConnectionsPerHost not working in iOS 10?

In a production app, I am setting the httpMaximumConnectionsPerHost property to 1 on a URLSessionConfiguration object when creating a URLSession. This allows me to queue a number of downloads, but only download 1 file at a time. Recently I noticed that all queued files download simultaneously on iOS 10 devices.


Has anyone else noticed this change? Is this intentional or a bug?


Note: I am setting the value of httpMaximumConnectionsPerHost both before and after creating the session, because of a post I found months ago that suggested that some properties of URLSessionConfiguration must be set after creating a URLSession in order for them to take effect.


    let configuration = URLSessionConfiguration.background(withIdentifier: sessionIdentifier)
    configuration.httpMaximumConnectionsPerHost = 1
   
    session = URLSession(
      configuration: configuration,
      delegate: self,
      delegateQueue: nil
    )
   
    session.configuration.httpMaximumConnectionsPerHost = 1

You’re doing this in a background session. What happens if you do it in a standard session?

I am setting the value of httpMaximumConnectionsPerHost both before and after creating the session, because of a post I found months ago that suggested that some properties of URLSessionConfiguration must be set after creating a URLSession in order for them to take effect.

That’s not something I’m aware of. The general model for session configurations is that, when you create a session from a configuration, the session should ‘latch’ a copy of the configuration and use that from then on. This hasn’t been implemented perfectly in all releases of the OS, but there’s no circumstances where setting configuration properties after creating the session is a good idea.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

A session created with URLSessionConfiguration.default has the same behavior. A background session is a requirement in this situation, as the files I'm downloading could be large & may have to be downloaded if the app isn't active.

A background session is a requirement in this situation …

Understood. I was just asking because it’s an interesting diagnostic test.

A session created with URLSessionConfiguration.default has the same behavior.

OK.

I just tried both of these situations on a device here in my office and

httpMaximumConnectionsPerHost
is working as I’d expect. Specifically:
  1. I started two downloads, A and B

  2. A immediately begins to make progress and B does not

  3. If I cancel A, B starts to make progress

Are you sure all of these downloads are targeting the same host?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I created a new project today from the "Single View Application" template. I replaced ViewController.swift with the following:


import UIKit
class ViewController: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    let url = URL(string: "REDACTED")!
    let request1 = URLRequest(url: url)
    let request2 = URLRequest(url: url)
    session.downloadTask(with: request1).resume()
    session.downloadTask(with: request2).resume()
  }
}
var session: URLSession = {
  let configuration = URLSessionConfiguration.background(withIdentifier: "DownloadTest")
  configuration.httpMaximumConnectionsPerHost = 1
  
  return URLSession(
    configuration: configuration,
    delegate: SessionWorker(),
    delegateQueue: nil
  )
}()
class SessionWorker: NSObject, URLSessionDownloadDelegate {
  func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
    print(downloadTask.taskIdentifier)
  }
  
  func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
  }
  
  func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    if let error = error {
      print(error)
    }
    
    if let response = task.response as? HTTPURLResponse {
      print(response.statusCode)
    }
  }
}


On iOS 9 (9.3.5, iPad Air 2 in my most recent test) devices, the didWriteData delegate method is called repeatedly for the first downloadTask, followed by a call to didCompleteWithError for the first downloadTask. Then, similar calls are made for the second downloadTask. On iOS 10 (10.2.1, iPhone 7 Plus in my most recent test), I receive calls many calls to didWriteData for both the first and second downloadTasks before a call to didCompleteWithError is made.


I have tested files from my company's servers, third party servers, and even the same URL for both downloadTasks. I have also tested on WIFI and LTE. All devices that download one file at a time are running some version of iOS 9. All devices that download many files at a time are running some version of iOS 10.


Was your test similar? Am I missing something?

Well, that seems pretty conclusive. You should definitely file a bug with your test project.

Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks, I've submitted bug report 30945888.

Any news with your bug report?

I have a similar issue with URLSessionUploadTask and URLSession with background configuration.

In iOS 11 beta this issue still exists... Is there a forecast when it will be fixed?

Will iOS 11 release without this bug?

Any news with your bug report?

No.

In iOS 11 beta this issue still exists...

I believe so, yes.

Is there a forecast when it will be fixed? Will iOS 11 release without this bug?

I can’t answer that, sorry.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Unfortunately, there hasn't been any activity on my bug report. 😟

I found out that, for background sessions, setting "timeoutIntervalForRequest" to a very high value actually makes the next task on queue to wait until the previous task is over (or until the value set at timeoutIntervalForRequest is over). This looks like a bug, but it worked for me.

If you don’t set anything, I think the default is 60 seconds, so the next task will start after 60 seconds, regardless of httpMaximumConnectionsPerHost being 1.

I just filed another radar for this exact same issue: 40215844 on iOS 11.3. This is still an issue.

It seems I am running into the same problem. I need to download multiple files from my server, and even when setting the max number of connections per host to 2.

Code Block
        let config = URLSessionConfiguration.background(withIdentifier: "background-download")
        config.sessionSendsLaunchEvents = true
        config.httpMaximumConnectionsPerHost = 2
        config.timeoutIntervalForRequest = 120
        let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)

all requests are hitting our server at the same time.

Is there any update on that issue?
Is this server HTTP/2?

Share and Enjoy

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

Hi Eskimo. At 2022 we are still experiencing this bug. can you suggest a workaround ? some more information

  1. unfortunately our server can process 1 request at a time. i think it uses java with a queue.
  2. right now we support ios 14.0 and above.
URLSessionConfiguration's httpMaximumConnectionsPerHost not working in iOS 10?
 
 
Q