Synchronous Url Request on Swift 2

Synchronous URL request on Swift 2

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)
}

Synchronous API request to Asynchronous API request Swift 2.2

Firstly, remove dispatch_semaphore related code from your function.

func getAuthentication(username: String, password: String){

let baseURL = "Some URL here"
let url = NSURL(string: baseURL)!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = "{\n \"username\": \"\(username)\",\n \"password\": \"\(password)\"\n}".dataUsingEncoding(NSUTF8StringEncoding);

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

if error == nil{
let swiftyJSON = JSON(data: data!)
print(swiftyJSON)

//parse the data to get the user
self.id = swiftyJSON["id"].intValue
self.token = swiftyJSON["meta"]["token"].stringValue
} else {
print("There was an error")
}
}
task.resume()
}

In the above code, the function dataTaskWithRequest itself is an asynchronus function. So, you don't need to call the function getAuthentication in a background thread.

For adding the completion handler,

func getAuthentication(username: String, password: String, completion:((sucess: Bool) -> Void)){

let baseURL = "Some URL here"
let url = NSURL(string: baseURL)!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = "{\n \"username\": \"\(username)\",\n \"password\": \"\(password)\"\n}".dataUsingEncoding(NSUTF8StringEncoding);

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

var successVal: Bool = true

if error == nil{
let swiftyJSON = JSON(data: data!)
print(swiftyJSON)
self.id = swiftyJSON["id"].intValue
self.token = swiftyJSON["meta"]["token"].stringValue
} else {
print("There was an error")
successVal = false
}

dispatch_async(dispatch_get_main_queue(), { () -> Void in
completion(successVal)
})
}
task.resume()
}

It can be called as follows:

self.getAuthentication("user", password: "password", completion:  {(success) -> Void in

})

Swift 3 - Send make synchronous http request

There is always a way to use the asynchronous pattern.

To make the function asynchronous add a completion block

func completeLoadAction(urlString:String, completion: (Int) -> ()) {
let url = URL(string:urlString.trimmingCharacters(in: .whitespaces))
let request = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
DispatchQueue.main.async {
let ac = UIAlertController(title: "Unable to complete", message: "The load has been added to the completion queue. This will be processed once there is a connection.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
}
completion(0) // or return an error code
return
}

let httpStatus = response as? HTTPURLResponse
var httpStatusCode:Int = (httpStatus?.statusCode)!

let responseString = String(data: data, encoding: .utf8)
print("responseString = \(responseString)")
DispatchQueue.main.async {
let ac = UIAlertController(title: "Completed Successfully", message: "The "+coldel+" has been completed successfully", preferredStyle: .alert)
ac.addAction(UIAlertAction(title:"Continue", style: .default, handler: { action in self.performSegue(withIdentifier: "segueConfirmedLoad", sender: self) }))
self.present(ac, animated: true)
}
completion(httpStatusCode)
}
task.resume()

}

and call it thusly

completeLoadAction(urlString: "www.something.com") { code in
print(code)
}

How to make synchronous url requests with swift 3

Your three-step problem can be solved via the use of a completion handler, i.e., a callback handler a la Node.js convention:

import Foundation
import Kitura
import HeliumLogger
import LoggerAPI

let session = URLSession(configuration: URLSessionConfiguration.default)

Log.logger = HeliumLogger()

let router = Router()

router.get("/test") { req, res, next in
let datatask = session.dataTask(with: URL(string: "http://www.example.com")!) { data, urlResponse, error in
try! res.send(data: data!).end()
}

datatask.resume()
}

Kitura.addHTTPServer(onPort: 3000, with: router)
Kitura.run()

This is a quick demo of a solution to your problem, and it is by no means following best Swift/Kitura practices. But, with the use of a completion handler, I am able to have my Kitura app make an HTTP call to fetch the resource at http://www.example.com, wait for the response, and then send the result back to my app's client.

Link to the relevant API: https://developer.apple.com/reference/foundation/urlsession/1410330-datatask

Performing a synchronous API-Call in Swift


let response = try Data(contentsOf: url)

As long as this is a command-line app, this is fine (and generally even pretty good). If this is an iOS app, this can crash the program if it takes too long to return, so don't do that. Use the async methods. They are not too complex; they solve the required problem. (This can be done on background queues, but generally shouldn't be. It causes thread-explosion.)

If you are using Swift 5.5 with async/await, then use URLSession.bytes(for:):

let response = await try urlSession.bytes(for: url)

This is appropriate in any async code. (It is not a "synchronous call," but it is written in that way.)

There is no solution where you can fetch data from the network in synchronous code on iOS on the main thread. That's by design. You must never block the main thread. No library or framework can change that. It would even be true in Python if you were using Python on an event loop that drives a UI. It's not a language issue. It's the fact that the main runloop needs to keep processing or the UI will hang.

Can you have synchronous but non-blocking URLSesssions?

Make it in background queue, like

DispatchQueue.global(qos: .background).async {
testServerRequest()
}

How can I convert a synchronous post request to an asynchronous post request?

You need to really make a lot of changes. Use swift type, instead of NSMutableURLRequest use URLRequest use String instead of NSString instead of NSDictionary & NSArray use Swift Dictionary and Array

func checkLogin () {
let username = txtUsername.text!
let password = txtPassword.text!

let post = "username=\(username)&password=\(password)"
NSLog("PostData: %@",post);
let url:URL = URL(string:"https://example.com/login.php")!
let postData = post.data(using: .utf8)!
let postLength = String( postData.count )
var request = URLRequest(url: url)

request.httpMethod = "POST"
request.httpBody = postData
request.setValue(postLength as String, forHTTPHeaderField: "Content-Length")
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil {
return
}
if let jsonData = (try? JSONSerialization.jsonObject(with: data!, options: [])) as? [String:Any] {
let success = jsonData["success"] as! Int
if success == 1 {
//do something,
}
else {
//show alert
}
}

})
task.resume()
}


Related Topics



Leave a reply



Submit