L N
L N

Reputation: 2968

timeoutIntervalForRequest of NSURLSessionConfiguration is not working with the background URLSession

I followed this tutorial Downloading files in background with URLSessionDownloadTask

And this Apple doc Downloading Files in the Background

I tried to setup because we have a long running API

let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).background")
    config.timeoutIntervalForRequest = 120
    config.timeoutIntervalForResource = 180

But I bumped into this weird issue:

If the server does not response with any piece of data, meaning that

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)

does not get called at all.

The app will kill off previous request and retry a new request every 66 seconds. I don't know where this number come from but from my experiment, it's roughly 66 seconds

If I set the timeoutIntervalForRequest = 10 the sure enough, the app will retry the request every 10 seconds but any attempt to set it above 66 seconds doesn't work

Not sure if anyone encounter the same issue and found a solution.

Just 1 note: the whole thing will timeout when it reach 180 seconds and the app stop retrying new requests

Upvotes: 3

Views: 767

Answers (1)

dgatwood
dgatwood

Reputation: 10417

First, that's a bug. Please file it at bugreport.apple.com. The timeout should not be ignored like that. Of course, there's a decent chance that this is a power management issue and won't be fixed, so I wouldn't hold your breath.

Second, you're approaching the problem in a way that is pretty much guaranteed to cause problems even if the timeout bug were fixed. The fact that your server isn't sending back any bytes to keep the connection alive is, of course, the reason the iOS device is disconnecting, but even if you change that and make it send out a bogus header one byte at a time every five seconds until the data is ready, you'll still have problems.

Basically, on a mobile device, you really shouldn't keep a long-running connection open to a remote server for any reason. It massively wastes battery to keep the Wi-Fi radio on continuously, much less the cellular radio, and worse, that connection could fail at any time when the user walks out of range, switches cell sites, or otherwise momentarily loses connectivity. Networks are crap — cellular networks doubly so.

A much better approach for long-running server processing is to do it asynchronously:

  • Make the request to the server.
  • Have the server send you back a unique identifier associated with the request, and, optionally, an estimated completion time.
  • Wait until the estimated completion time, then ask the server how things are going (providing that unique identifier).
  • Continue to periodically poll the server until the server says that the task is completed (or has failed).
  • When the server says that the task is completed, issue a request to retrieve the results, then issue a request to free the completed results.
  • Periodically clean up old, uncollected results on the server with a cron job or similar.

This approach avoids keeping the radio hot except for a couple of seconds on either side of your polling requests, and it makes the timeout issue entirely moot.

Upvotes: 1

Related Questions