7

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!

Leo Natan
  • 55,734
  • 8
  • 140
  • 186
askaale
  • 1,067
  • 1
  • 19
  • 41
  • 1
    It is already clear this question is about Swift by the tags. What is the point of having "Swift" in the title? – Leo Natan Oct 21 '16 at 13:53
  • 1
    @LeoNatan Rather unnecessary to -1 my post due to the fact that you think it's wrong of me to put 'Swift' in the title. But to answer your question, it's simply because I just want to point out that the language is Swift. Something I always do. – askaale Oct 21 '16 at 13:57
  • 1
    You have already pointed out it's in Swift - you have the `swift` and `swift3` tags. Also, this is a framework question. Your problem has nothing to do with Swift. Do you really not want to accept help if it comes in ObjC form? – Leo Natan Oct 21 '16 at 14:01
  • 2
    @LeoNatan Okay, from now on I will avoid doing this. – askaale Oct 21 '16 at 14:04

1 Answers1

3

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.

dgatwood
  • 9,519
  • 1
  • 24
  • 48
  • 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? – askaale Oct 21 '16 at 16:50
  • 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 Oct 21 '16 at 18:09
  • 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. – dgatwood Oct 21 '16 at 18:10
  • 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. – askaale Oct 21 '16 at 18:23