URLSession dataTask timeout error

12,882

Timeouts should always several minutes long, not twenty seconds. It can take twenty or thirty seconds just to do a DNS lookup on a bad cellular connection, and using a twenty-second timeout means that on a poor network, your app will be completely unusable.

The way you handle this in the UI should be entirely independent of the networking code. Start the request and simultaneously create and start a timer. If your session delegate hasn't gotten a ...didReceiveResponse:... call by the time the timer fires, show a "slow network" UI, but let the network request continue until it fails.

Additionally, if your requests are idempotent (that is, if issuing the request twice is safe), you might consider having a second timer with a very short interval (say 5 seconds), and if you haven't gotten a didReceiveResponse: call within that time, start a second request in parallel. If that second task doesn't get a response by the time your twenty-second timer fires, cancel it. If it gets a response first, cancel the original request. This can help reduce the impact of packet loss on user-perceived latency.

Share:
12,882
askaale
Author by

askaale

Updated on June 07, 2022

Comments

  • askaale
    askaale almost 2 years

    I am currently experiencing some issues regarding the URLSession, while trying to post data to my web server. This however, works perfectly. What seems to not work, is the timeout I have set. This is rather crucial for my whole app, as I don't want the users to be 'loading' forever, without any type of error message. Here is my code:

    var request = URLRequest(url: URL(string: "https://www.mywebsite.com/file.php")!, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 20)
    
    let urlconfig = URLSessionConfiguration.default
    urlconfig.timeoutIntervalForRequest = 20
    urlconfig.timeoutIntervalForResource = 20
    
    request.httpMethod = "POST"
    let session = URLSession(configuration: urlconfig, delegate: self, delegateQueue: nil)//URLSession.shared
    let body = "receiver=\(receiverID)"
    request.httpBody = body.data(using: String.Encoding.utf8, allowLossyConversion: true)
    request.timeoutInterval = 20
    
    session.dataTask(with: request) {data, response, err in
        if err == nil {
            do {
                let jsonResult:NSDictionary? =  try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
                let jsonComp = jsonResult?.value(forKey: "completion") as! String
                if jsonComp == "done" {
                } else {
                }
            }catch{}
        } else {
        }
    }.resume()
    

    I simply want to set the timeout to 20 seconds, and then return an error (an alert). How can I do this? I feel that I have tried everything possible, just by configuration the URLSessionConfiguration, and set the .timeoutInterval.

    Help would be greatly appreciated!

  • askaale
    askaale over 7 years
    Okay, thanks for sorting this out. I get it! However, are you telling me that after x amount time, the request will eventually fail? So if I create an alert in 'if error == nil { } else { print("something here") }', the console will print 'Something here'? And also, could I potentially just cancel the request after x amount of seconds, through my timer?
  • dgatwood
    dgatwood over 7 years
    Eventually, it might fail, in which case yes, you'd get called with a non-nil error. I think the default is to give up 90 seconds without receiving a single byte of data, but I might be remembering the number wrong. The default is to not have a maximum total time at all, because that would cause longer downloads to fail.
  • dgatwood
    dgatwood over 7 years
    You could eventually cancel the request, but you probably shouldn't. The general recommendation from Apple is to let the user cancel the request if the user no longer cares about the data. Otherwise, assume that the user eventually wants to get the data, even if it takes a long time.
  • askaale
    askaale over 7 years
    I see. I am actually doing a transaction through Stripe. So I think I'll stick with a 70 seconds timeout, in case some people are afraid of something happening to their money.