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
Looping Through Nsattributedstring Attributes to Increase Font Size
Rebuild an Nsarray by Grouping Objects That Have Matching Id Numbers
Update Label from Background Timer
Xcode iOS Project Only Shows "My MAC 64-Bit" But Not Simulator or Device
Custom Uitableviewcell Programmatically Using Swift
How to Prompt the User to Turn on Location Services After User Has Denied Their Use
How to Open Fb and Instagram App by Tapping on Button in Swift
How to Change Uitableviewrowaction Title Color
How to Convert a Nib-Based Project to a Storyboard-Based
Open Uitableview Edit Action Buttons Programmatically
Swiftui Hide Tabview Bar Inside Navigationlink Views
Is There an iPhone Se Simulator for Xcode 11, iOS 13
Uiview - How to Get Notified When the View Is Loaded
Alternative iOS Layouts for Portrait and Landscape Using Just One .Xib File
How to Remove Cache in Wkwebview
iOS Game Center: Scores Not Showing on Leaderboard in Sandbox