Bryan Cimo Bryan Cimo - 5 months ago 84
iOS Question

In Swift, when using dataTaskWithRequest the completionHandler returns a nil NSURLResponse when the request times out

When I post data to a service, the request is normally fine, but if it takes too long and times out, in the completion handler, I get NSURLResponse as nil even though it should never return nil.

I'm using Swift 1.1, here's an example of how I'm doing this:

func postX(actionKey:String, postData:AnyObject, callBack:((data:NSData?, resp: NSURLResponse) -> Void)?) -> Void
{
var mreq = createRequest(actionKey, method: "POST", https: true, json: true)
if (self.dataTask != nil)
{
self.dataTask?.cancel()
}
var err: NSError?
mreq.HTTPBody = NSJSONSerialization.dataWithJSONObject(postData, options: nil, error: &err)
self.dataTask = self.getSession().dataTaskWithRequest(mreq, completionHandler: { (data:NSData!, resp: NSURLResponse!, error: NSError!) -> Void in

Dlog.log("response: \(resp)") //Prints out: response: nil
if (error != nil)
{
//do something
}
else
{
//do something else
}
})
if (self.dataTask != nil)
{
self.dataTask!.resume()
}
}

Rob Rob
Answer

The NSURLResponse reference in the completionHandler block of dataTaskWithRequest is an optional (meaning that it can be nil). If the request times out, you'd expect it to be nil (because you presumably have not yet received any response).

I would suggest changing the NSURLResponse parameter of your callback closure to be an optional as well, just like dataTaskWithRequest. (I might return the optional NSError, too, so you can check for particular errors.) And you can detect timeout errors by looking for NSURLErrorTimedOut.

For example:

func postX(actionKey: String, postData: AnyObject, callBack: (NSData?, NSURLResponse?, NSError?) -> ()) {
    let mreq = createRequest(actionKey, method: "POST", https: true, json: true)

    dataTask?.cancel()   // note, we don't need `if` clause, as the `?` does everything for us

    mreq.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(postData, options: nil)

    dataTask = self.getSession().dataTaskWithRequest(mreq) { data, response, error in
        callBack?(data, response, error)
    }

    dataTask.resume()
}

Then you could use it like:

postX(actionKey, postData: postData) { data, response, error in

    // handle response however you want

    // did it time out?

    if error != nil {
        if error!.domain == NSURLErrorDomain && error!.code == NSURLErrorTimedOut {
            print("timed out") // note, `response` is likely `nil` if it timed out
        }
    }

}