Fixing NSURLConnection Deprecation from Swift 1.2

2019-01-12 12:11发布

问题:

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. :)

回答1:

(Repeating my arguments from https://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.