Simpliest solution to check if File exists on a webserver. (Swift)

4.9k views Asked by At

There are a lot of discussion about this and I understand the solution to use the delegate method and check the response "404":

var request : NSURLRequest = NSURLRequest(URL: url)

var connection : NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)!
connection.start()

    func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
//...
}

But I would like to have a simple solution like:

var exists:Bool=fileexists(sURL);

Because I will have a lot of request in the same class with the delegate and I only want to check the response with my function fileexists().

Any hints ?

UPDATE I guess I'll have to do a synchronious request like the following, but I get always 0x0000000000000000 as a response::

   let urlPath: String = sURL;
    var url: NSURL = NSURL(string: urlPath)!
    var request1: NSURLRequest = NSURLRequest(URL: url)
    var response: AutoreleasingUnsafeMutablePointer<NSURLResponse?
    >=nil
    var error: NSErrorPointer = nil
    var dataVal: NSData =  NSURLConnection.sendSynchronousRequest(request1, returningResponse: response, error:nil)!
    var err: NSError
    println(response)
3

There are 3 answers

2
Martin R On BEST ANSWER

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.

All that being said, here is a possible implementation for a fileExists() method. You should not use it on the main thread, you have been warned!

The HTTP request method is set to "HEAD", so that the server sends only the response header, but no data.

func fileExists(url : NSURL!) -> Bool {

    let req = NSMutableURLRequest(URL: url)
    req.HTTPMethod = "HEAD"
    req.timeoutInterval = 1.0 // Adjust to your needs

    var response : NSURLResponse?
    NSURLConnection.sendSynchronousRequest(req, returningResponse: &response, error: nil)

    return ((response as? NSHTTPURLResponse)?.statusCode ?? -1) == 200
}
0
Islam On

Improved Vito's solution so the completion is always called:

func fileExists(at url: URL, completion: @escaping (Bool) -> Void) {
    var request = URLRequest(url: url)
    request.httpMethod = "HEAD"
    request.timeoutInterval = 1.0 // Adjust to your needs
    URLSession.shared.dataTask(with: request) { _, response, _ in
        completion((response as? HTTPURLResponse)?.statusCode == 200)
    }.resume()
}

// Usage
fileExists(at: url) { exists in
    if exists {
        // do something
    }
}

async/await

func fileExists(at url: URL) async throws -> Bool {
    var request = URLRequest(url: url)
    request.httpMethod = "HEAD"
    request.timeoutInterval = 1.0 // Adjust to your needs
    let (_, response) = try await URLSession.shared.data(for: request)
    return (response as? HTTPURLResponse)?.statusCode == 200
}

// Usage
if try await fileExists(at: url) {
    // do something
}

// or if you don't want to deal with the `throw`
if (try? await fileExists(at: url)) ?? false {
    // do something
}
1
Vito Royeca On

Swift 3.0 version of Martin R's answer written asynchronously (the main thread isn't blocked):

func fileExistsAt(url : URL, completion: @escaping (Bool) -> Void) {
    let checkSession = Foundation.URLSession.shared
    var request = URLRequest(url: url)
    request.httpMethod = "HEAD"
    request.timeoutInterval = 1.0 // Adjust to your needs

    let task = checkSession.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
        if let httpResp: HTTPURLResponse = response as? HTTPURLResponse {
            completion(httpResp.statusCode == 200)
        }
    })

    task.resume()
}