title: Why is NSURLSession slower than cURL when downloading many files?
body: <p>I've been using <code>cURL</code> to download about 1700+ files -- which total to about ~290MB -- in my iOS app. It takes about 5-7 minutes on my Internet connection to download all of them using <code>cURL</code>. But since not everyone has fast internet connection (especially when on the go), I decided to allow the files to be downloaded in the background, so that the user can do other things while waiting for the download to finish. This is where <code>NSURLSession</code> comes in.</p>
  
  <p>Using <code>NSURLSession</code>, it takes about 20+ minutes on my Internet connection to download all of them <em>while the app is in foreground</em>. I don't mind it being slow when the app is in background, because I understand that it is up to the OS to schedule the downloads. But it's a problem when it's slow even when it's in foreground. Is this the expected behaviour? Is it because of the quantity of the files?</p>
  
  <p>In case I'm not using <code>NSURLSession</code> correctly, here's a snippet of how I'm using it:</p>
  
  <pre><code>// Initialization
  
  NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"&lt;my-identifier&gt;"];
  sessionConfiguration.HTTPMaximumConnectionsPerHost = 40;
  
  backgroundSession = [NSURLSession sessionWithConfiguration:sessionConfiguration
                                                    delegate:self
                                               delegateQueue:nil];
  
  // ...
  
  // Creating the tasks and starting the download
  for (int i = 0; i &lt; 20 &amp;&amp; queuedRequests.count &gt; 0; i++) {
      NSDictionary *requestInfo = [queuedRequests lastObject];
      NSURLSessionDownloadTask *downloadTask = [backgroundSession downloadTaskWithURL:[NSURL URLWithString:requestInfo[@"url"]]];
      ongoingRequests[@(downloadTask.taskIdentifier)] = requestInfo;
      [downloadTask resume];
      [queuedRequests removeLastObject];
      NSLog(@"Begin download file %d/%d: %@", allRequests.count - queuedRequests.count, allRequests.count, requestInfo[@"url"]);
  }
  
  // ...
  
  // Somewhere in (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
  
  // After each download task is completed, grab a file to download from
  // queuedRequests, and create another task
  
  if (queuedRequests.count &gt; 0) {
      requestInfo = [queuedRequests lastObject];
      NSURLSessionDownloadTask *newDownloadTask = [backgroundSession downloadTaskWithURL:[NSURL URLWithString:requestInfo[@"url"]]];
      ongoingRequests[@(newDownloadTask.taskIdentifier)] = requestInfo;
  
      [newDownloadTask resume];
      [queuedRequests removeLastObject];
      NSLog(@"Begin download file %d/%d: %@", allRequests.count - queuedRequests.count, allRequests.count, requestInfo[@"url"]);
  }
  </code></pre>
  
  <p>I've also tried using multiple <code>NSURLSession</code>, but it's still slow. The reason I tried that is because when using <code>cURL</code>, I create multiple threads (around 20), and each thread will download a single file at a time.</p>
  
  <p>It's also not possible for me to reduce the number of files by zipping it, because I need the app to be able to download individual files since I will update them from time to time. Basically, when the app starts, it will check if there are any files that have been updated, and only download those files. Since the files are stored in S3, and S3 doesn't have zipping service, I could not zip them into a single file on the fly.</p>