RoNiT RoNiT - 11 days ago 7
Swift Question

Fixing NSURLConnection Deprecation from Swift 1.2 to 2.0

I have a function written in Swift 1.2, that checks for Reachability of Address or IP. Here it is :

func isHostConnected(hostAddress : String) -> Bool
{
var response : NSURLResponse?
let request = NSMutableURLRequest(URL: NSURL(string: hostAddress)!)
request.timeoutInterval = 3

let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: nil)

return ((response as? NSHTTPURLResponse)!.statusCode == 200)
}


Now since,
NSURLConnection
is deprecated, as per Xcode suggestion I tried writing it using
NSURLSession.dataTaskWithRequest
, here it is :

func isHostConnected(hostAddress : String) -> Bool
{
let request = NSMutableURLRequest(URL: NSURL(string: hostAddress.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!)!)
request.timeoutInterval = 3

let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: nil, delegateQueue: NSOperationQueue.mainQueue())

var responseCode = -1

session.dataTaskWithRequest(request, completionHandler: {(data : NSData?, response : NSURLResponse?, error : NSError?) -> Void in
responseCode = (response as? NSHTTPURLResponse)!.statusCode
})!.resume()

return (responseCode == 200)
}


Above code always returns false (its Obvious), since
completionHandler
gets executed on seperate Thread.

My concern is that, can I make
completionHandler()
run on MainThread somehow like
sendSynchronousRequest
does by blocking it.

I have reasons to not to use 'Apple's Reachabilty' here.

Any suggestion will be helpful. :)

Answer

(Repeating my arguments from http://stackoverflow.com/a/30670518/1187415:)

Checking if a resource exists on a server requires sending a HTTP request and receiving the response. TCP communication can take some amount of time, e.g. if the server is busy, some router between the client and the server does not work correctly, the network is down etc.

That's why asynchronous requests are always preferred. Even if you think that the request should take only milliseconds, it might sometimes be seconds due to some network problems. And – as we all know – blocking the main thread for some seconds is a big no-no.

That being said, you can use a "counting semaphore" or a "dispatch group" to wait for the completion of some asynchronous task. You should not use this on the main thread. Blocking the main thread for up to 3 seconds is not acceptable!

func isHostConnected(hostAddress : String) -> Bool
{
    let request = NSMutableURLRequest(URL: NSURL(string: hostAddress.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!)!)
    request.timeoutInterval = 3
    request.HTTPMethod = "HEAD"

    let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
    var responseCode = -1

    let group = dispatch_group_create()
    dispatch_group_enter(group)

    session.dataTaskWithRequest(request, completionHandler: {(_, response, _) in
        if let httpResponse = response as? NSHTTPURLResponse {
            responseCode = httpResponse.statusCode
        }
        dispatch_group_leave(group)
    })!.resume()

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
    return (responseCode == 200)
}

Remarks:

  • Setting the HTTP method to "HEAD" is a small optimization, as the server sends only the response header without the actual data.
  • In the case of a illegal host name, response would be nil, and responseCode = (response as? NSHTTPURLResponse)!.statusCode would crash.
Comments