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