By SpaceDog


2015-07-22 08:22:28 8 Comments

I have this code from here to do synchronous request of a URL on Swift 2.

  func send(url: String, f: (String)-> ()) {
    var request = NSURLRequest(URL: NSURL(string: url)!)
    var response: NSURLResponse?
    var error: NSErrorPointer = nil
    var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: error)
    var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
    f(reply)
  }

but the function NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: error) was deprecated and I don't see how one can do synchronous requests on Swift, cause the alternative is asynchronous. Apparently Apple deprecated the only function that can do it synchronously.

How can I do that?

4 comments

@fpg1503 2015-07-22 12:27:05

If you really wanna do it synchronously you can always use a semaphore:

func send(url: String, f: (String) -> Void) {
    var request = NSURLRequest(URL: NSURL(string: url)!)
    var error: NSErrorPointer = nil
    var data: NSData

    var semaphore = dispatch_semaphore_create(0)

    try! NSURLSession.sharedSession().dataTaskWithRequest(request) { (responseData, _, _) -> Void in
        data = responseData! //treat optionals properly
        dispatch_semaphore_signal(semaphore)
    }.resume()

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

    var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
    f(reply)
}

EDIT: Add some hackish ! so the code works, don't do this in production code

Swift 3.0+ (3.0, 3.1, 3.2, 4.0)

func send(url: String, f: (String) -> Void) {
    guard let url = URL(string: url) else {
        print("Error! Invalid URL!") //Do something else
        return
    }

    let request = URLRequest(url: url)
    let semaphore = DispatchSemaphore(value: 0)

    var data: Data? = nil

    URLSession.shared.dataTask(with: request) { (responseData, _, _) -> Void in
        data = responseData
        semaphore.signal()
    }.resume()

    semaphore.wait(timeout: .distantFuture)

    let reply = data.flatMap { String(data: $0, encoding: .utf8) } ?? ""
    f(reply)
}

@SpaceDog 2015-07-22 12:44:35

Xcode 7b4 is complaining about a lot of things on your code.

@SpaceDog 2016-01-18 16:01:13

what down vote? I have not down voted you.

@Vinod Radhakrishnan 2018-03-21 09:53:09

I think it's better to use "dispatchGroup"

@Matej Ukmar 2016-11-09 15:19:17

Based on @fpg1503 answer I made a simple extension in Swift 3:

extension URLSession {

    func synchronousDataTask(with request: URLRequest) throws -> (data: Data?, response: HTTPURLResponse?) {

        let semaphore = DispatchSemaphore(value: 0)

        var responseData: Data?
        var theResponse: URLResponse?
        var theError: Error?

        dataTask(with: request) { (data, response, error) -> Void in

            responseData = data
            theResponse = response
            theError = error

            semaphore.signal()

        }.resume()

        _ = semaphore.wait(timeout: .distantFuture)

        if let error = theError {
            throw error
        }

        return (data: responseData, response: theResponse as! HTTPURLResponse?)

    }

}

Then you simply call:

let (data, response) = try URLSession.shared.synchronousDataTask(with: request)

@SpaceDog 2016-11-09 16:59:36

brilliant! thanks!

@Tom Andersen 2016-08-02 21:02:20

Synchronous requests are sometimes fine on background threads. Sometimes you have a complicated, impossible to change code base full of async requests, etc. Then there is a small request that can't be folded into the current system as async. If the sync fails, then you get no data. Simple. It mimics how the file system works.

Sure it does not cover all sorts of eventualities, but there are lots of eventualities not covered in async as well.

@Jiri Trecak 2015-07-22 11:34:29

There is a reason behind deprecation - there is just no use for it. You should avoid synchronous network requests as a plague. It has two main problems and only one advantage (it is easy to use.. but isn't async as well?):

  • The request blocks your UI if not called from different thread, but if you do that, why don't use asynchronous handler right away?
  • There is no way how to cancel that request except when it errors on its own

Instead of this, just use asynchronous request:

NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in

    // Handle incoming data like you would in synchronous request
    var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
    f(reply)
})

iOS9 Deprecation

Since in iOS9 this method is being deprecated, I suggest you to use NSURLSession instead:

let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request) { (data, response, error) -> Void in

    // Handle incoming data like you would in synchronous request
    var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
    f(reply)
}

@SpaceDog 2015-07-22 12:03:57

that function was deprecated on iOS 9

@Jiri Trecak 2015-07-22 12:07:24

I edited my post with your note. Thank you

@Pierre Lebeaupin 2015-07-22 13:48:10

For a longer exposition on why synchronous networking is bad, look at devforums.apple.com/thread/9606?tstart=0 (requires iOS dev forums access)

@SpaceDog 2015-07-22 16:01:36

working now, thanks!!!

@olivaresF 2016-02-02 00:43:28

I feel like, even though you're correct, the question was looking for a way to do synchronous networking and this isn't an answer.

@Jiri Trecak 2016-02-02 12:12:15

SO primary purpose is to give you a GOOD advice. Advising someone to even consider using synch requests is just.. bad. I understand you opinion and value it, but I feel this is exactly the mindset that hinders the quality of this site. At minimum, it is definitely not worth downvoting :)

@Janusz Chudzynski 2016-05-12 02:23:37

What if you need to have a network call inside a NSOperation's main method? That's where synchronous networking is justified.

Related Questions

Sponsored Content

0 Answered Questions

Objective C UNIRest redirection url

  • 2018-10-15 03:08:14
  • 오근택
  • 21 View
  • 0 Score
  • 0 Answer
  • Tags:   ios http unirest

1 Answered Questions

0 Answered Questions

Parse push json IOS webview

1 Answered Questions

[SOLVED] Swift AsynchronousRequest response cannot be read

  • 2016-01-22 12:53:52
  • Erik Laan
  • 77 View
  • 0 Score
  • 1 Answer
  • Tags:   ios database swift

1 Answered Questions

[SOLVED] how to use JsonArray out of Queue

3 Answered Questions

[SOLVED] How do you check if NSURLRequest.URL is nil?

  • 2014-11-14 20:37:57
  • whitneyland
  • 2004 View
  • 0 Score
  • 3 Answer
  • Tags:   ios swift

1 Answered Questions

[SOLVED] how to add json parse data in core data in iOS?

2 Answered Questions

[SOLVED] setbackground loading with do while loop

3 Answered Questions

Sponsored Content