Code executed in the wrong order - swift

My code ran in the wrong order. The aerror was printed at the beginning, it should be printed at the end.
Here is my code
var aerror :Int?
NSURLConnection.sendAsynchronousRequest(NSURLRequest(URL: url), queue: NSOperationQueue.mainQueue(), completionHandler: { (res, data, error) -> Void in
let str = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("\n********\nData\n*******")
print(str)
print("\n********\nData\n*******")
let json = JSON(data: data!)
aerror = json["Logon"]["error"].int!
print("\n********\njson\n*******")
print(json)
print("\n********\njson\n*******")
})
print("\n********\naeeror=\(aerror)")
Here is output
aeeror=nil
********
Data
*******
Optional({"func":"LogonJs","Logon":{"error":2}})
********
Data
*******
********
json
*******
{
"Logon" : {
"error" : 2
},
"func" : "LogonJs"
}
********
json
*******
I don't know why the aerror was printed first.
I Want print the aerror before I return it.
Can anyone help me fix it?

NSURLConnection.sendAsynchronousRequest will start an asynchronous request. meaning that there is no way of telling when it will complete at the moment it is called, and once it gets called the next statement will be executed.
The method you are passing it (the completionHandler) is run once the asynchronous request finishes.
In simple terms what is happening is this :
->call sendAsynchronousRequest (with a completionhandler)
->print("\n********\naeeror=\(aerror)") (the asynchronous request is running in the background)
->the asynchronous request finishes
->completionHandler is executed
since you assign aerror in the completion handler, you can print it from the completion handler too.
var aerror :Int?
NSURLConnection.sendAsynchronousRequest(NSURLRequest(URL: url), queue: NSOperationQueue.mainQueue(), completionHandler: { (res, data, error) -> Void in
let str = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("\n********\nData\n*******")
print(str)
print("\n********\nData\n*******")
let json = JSON(data: data!)
aerror = json["Logon"]["error"].int!
print("\n********\njson\n*******")
print(json)
print("\n********\njson\n*******")
print("\n********\naeeror=\(aerror)")
})

Everything is working fine...
NSURLConnection.sendAsynchronousRequest as stated ub the method name send an asyncronows request with a completion handler that will be executed once the response is received.
What your code actually do is:
Create the request
send it moving the entire process of "sending", "waiting" and "managing the response(which means running the completion handler)"on a different thread
Print error on the main thread
This means that once sendAsynchronousRequest is invoked the program doesn't wait for it to finish and execute the compltion handler but keep going and print "aerror". That is the entire purpose of asynchronicity!
If you want your main thread to wait for the termination of the completion handler before continuing the execution I'd suggest the use of a semaphore or the method
sendSynchronousRequest(_:returningResponse:)
Anyway you should use NSURLSession instead of NSURLConnection because this last one is now deprecated.

Related

No response when doing a GET-Request with Alamofire [duplicate]

In a swift 2 command line tool (main.swift), I have the following:
import Foundation
print("yay")
var request = HTTPTask()
request.GET("http://www.stackoverflow.com", parameters: nil, completionHandler: {(response: HTTPResponse) in
if let err = response.error {
print("error: \(err.localizedDescription)")
return //also notify app of failure as needed
}
if let data = response.responseObject as? NSData {
let str = NSString(data: data, encoding: NSUTF8StringEncoding)
print("response: \(str)") //prints the HTML of the page
}
})
The console shows 'yay' and then exits (Program ended with exit code: 0), seemingly without ever waiting for the request to complete. How would I prevent this from happening?
The code is using swiftHTTP
I think I might need an NSRunLoop but there is no swift example
Adding RunLoop.main.run() to the end of the file is one option. More info on another approach using a semaphore here
I realize this is an old question, but here is the solution I ended on. Using DispatchGroup.
let dispatchGroup = DispatchGroup()
for someItem in items {
dispatchGroup.enter()
doSomeAsyncWork(item: someItem) {
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: DispatchQueue.main) {
exit(EXIT_SUCCESS)
}
dispatchMain()
You can call dispatchMain() at the end of main. That runs the GCD main queue dispatcher and never returns so it will prevent the main thread from exiting. Then you just need to explicitly call exit() to exit the application when you are ready (otherwise the command line app will hang).
import Foundation
let url = URL(string:"http://www.stackoverflow.com")!
let dataTask = URLSession.shared.dataTask(with:url) { (data, response, error) in
// handle the network response
print("data=\(data)")
print("response=\(response)")
print("error=\(error)")
// explicitly exit the program after response is handled
exit(EXIT_SUCCESS)
}
dataTask.resume()
// Run GCD main dispatcher, this function never returns, call exit() elsewhere to quit the program or it will hang
dispatchMain()
Don't depend on timing.. You should try this
let sema = DispatchSemaphore(value: 0)
let url = URL(string: "https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_November_2010-1a.jpg")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
print("after image is downloaded")
// signals the process to continue
sema.signal()
}
task.resume()
// sets the process to wait
sema.wait()
If your need isn't something that requires "production level" code but some quick experiment or a tryout of a piece of code, you can do it like this :
SWIFT 3
//put at the end of your main file
RunLoop.main.run(until: Date(timeIntervalSinceNow: 15)) //will run your app for 15 seconds only
More info : https://stackoverflow.com/a/40870157/469614
Please note that you shouldn't rely on fixed execution time in your architecture.
Swift 4: RunLoop.main.run()
At the end of your file
// Step 1: Add isDone global flag
var isDone = false
// Step 2: Set isDone to true in callback
request.GET(...) {
...
isDone = true
}
// Step 3: Add waiting block at the end of code
while(!isDone) {
// run your code for 0.1 second
RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.1))
}

Control when a different number of requests are completed in Swift

I have the following code that runs about :
for resFoto in resFotosResenhaEscolhidas {
jsonRequestUploadImagem = ResFotosModel.createJsonFotoResenha(resFoto)
let requestUploadImagem: NSMutableURLRequest = serviceRepository.clientURLRequest(wsUploadImagem, typeAutho: .basic, parms: "", body: jsonRequestUploadImagem as Dictionary<String, AnyObject>)
serviceRepository.post(requestUploadImagem, retryLogin: true, completion: {isOk,msgError,httpCode,needLogin,response in
self.checkResponseUploadImagemFotoResenha(response as AnyObject, httpCode)
})
}
func checkResponseUploadImagemFotoResenha(_ response:AnyObject, _ httpCode:Int) {
if httpCode != 200 {
let string = String(data: response as! Data, encoding: .utf8)
print( string!+" \n Erro HTTP: "+String(httpCode) )
} else {
// httpCode == 200
let data: Data = response as! Data // received from a network request, for example
let jsonResponse = try? JSONSerialization.jsonObject(with: data, options: [])
print("json response upload foto")
print(jsonResponse!)
}
}
The serviceRepository.post just run a "urlSession.dataTask", but I wanna know how can I control when the completion of the request.
The "resFotosResenhaEscolhidas" object contains 0 to 4 array inside it depending on the call. So, the code runs and create from 0 to 4 requests.
If the 4 requests are running, I just wanna know how can I check when they are finished?
Look at using a DispatchGroup. You'd create a DispatchGroup when you get ready to begin making network calls.
You'd call enter() on your dispatch group each time you begin a new URLSession task (or other async task.) You'd call leave() on the dispatch group in the completion handler for each task.
After you've submitted your async tasks, you'd call the dispatch group's notify() method to submit a block that will execute once all your async tasks are complete. (It's important that you wait until you've submitted your async tasks before calling notify(). If you try to call it before submitting your tasks, it invokes it's closure immediately since no tasks are running.)
I wrote a little demo project that uses a DispatchGroup to monitor a set of async tasks. (In the demo the tasks just delay for a random time before completing, and generate a random number.)
It waits until they've all completed, and then indicates the task that returned the largest value.
You can check it out on Github: DispatchGroupDemo on github

POST Request with URLSession in Swift in command line app [duplicate]

This question already has answers here:
Using NSURLSession from a Swift command line program
(3 answers)
Closed 2 years ago.
I am trying to send HTTP request with POST method in a command line app. Using JSON as body of the request. I am using session.uploadTask to send this request and use JSON data serialised from simple Dictionary. Maybe I missed something but it doesn't work. I even tried to write my request to console and it looks good -> it is the same format as iTranslate API wants.
//creating new session
let session = URLSession.shared
let url = URL(string: "https://api.itranslate.com/translate/v1")!
//setting httpMethod to POST
var request = URLRequest(url: url)
request.httpMethod = "POST"
//setting header
request.setValue("application/json", forHTTPHeaderField: "content-type")
//dictionary with json
let json = ["key": "...", "source": ["dialect":"en", "text": "How are you?"], "target": ["dialect": "es"]] as [String : Any]
//serialization from json to jsonData
let jsonData = try! JSONSerialization.data(withJSONObject: json, options: [])
let task = session.uploadTask(with: request, from: jsonData) { data, response, error in
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print(dataString)
}
}
task.resume()
In most apps, there is a “run loop” that is constantly running, responding to user interaction, keeping the app alive until the user explicitly terminates it. In that scenario, we can initiate the asynchronous network request, confident that the run loop is keeping our app alive, and when the network response comes in, our completion handler closure is called.
In a command line app, on the other hand, when it gets to the end of the code, the app simply terminates, never giving the asynchronous network request a chance to respond, and the closure will never be called.
You can, however, have the command line app wait for the request to finish before terminating, e.g.
let semaphore = DispatchSemaphore(value: 0)
let task = session.uploadTask(with: request, from: jsonData) { data, response, error in
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print(dataString)
}
semaphore.signal()
}
task.resume()
semaphore.wait()
Now, this technique (waiting on the main thread, thereby blocking that thread) is a very bad practice in standard apps, because it will block the main thread while the network request is running. In a typical app, blocking the main thread would freeze the UI, offering a substandard user experience (and the OS might even kill the app for not being responsive). We generally would never block the main thread.
But in a command line app, where you often don't worry about a graceful and responsive UI, blocking the main thread is less of an issue and the above can keep the app alive while the network request is running.

REST API calls not working in swift

I'm following this tutorial for making a simple REST API call in swift: https://grokswift.com/simple-rest-with-swift/
The problem I'm running into is that the data task completion handler next gets executed. When I'm debugging it step by step, it just jumps over the completion handler block. Nothing is printed in the console, either.
I've searched for other methods of making REST API calls, but they are all very similar to this one and not working, either.
Here is my code:
let endpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
guard let url = URL(string: endpoint) else {
return
}
let urlRequest = URLRequest(url: url)
let session = URLSession.shared
let task = session.dataTask(with: urlRequest) { (data, response, error) -> Void in
guard error == nil else {
print("Error calling GET")
return
}
guard let responseData = data else {
print("Error receiving data")
return
}
do {
print ("Parsing response...")
}
}
task.resume()
Your code looks right to me. I tested it in a Playground and I'm getting the Parsing response... message printed to the console which makes me think the issue is elsewhere in your code or environment. I'd be happy to take a look at the whole project if you can post a Github link or something similar.
Here are the steps I would take to debug an issue like this:
1) Confirm my execution environment has an active internet connection. The Safari app can be used to confirm on iOS devices or the Simulator. Playgrounds can be tested by pasting the following lines.
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
print (try? String(contentsOf: url))
Look for a line in the console output similar to:
Optional("{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false\n}")
2) Confirm the url is valid and returns data by pasting it into a web browser url bar and hitting enter. You will either see JSON printed in the browser or not.
3) Confirm my code is actually getting called when the application runs. You can do this with either breakpoints or print() statements. As OOPer2 pointed out asynchronous callback closures like that used in session.dataTask() execute in a different time than the rest of your code which is why "it just jumps over the completion handler block" while stepping through with the debugger. You'll need to put another breakpoint or print() statement inside the completion handler closure. I'd put the breakpoint on the guard error == nil else { line.
4) Make sure the application is still executing when the network request finishes and the completion handler closure executes. If your code is in a ViewController running in an iOS application it's probably fine, but if it's running in a Playground it may not be. Playgrounds by default stop execution once the last line of code has been evaluated which means the completion closure will never execute. You can tell a Playground to continue executing indefinitely by importing the PlaygroundSupport framework and setting needsIndefiniteExecution = true on the current Playground page. Paste the entire code block below into a Playground to see it in action:
import Foundation
import PlaygroundSupport
// Keep executing the program after the last line has evaluated so the
// closure can execute when the asynchronous network request finishes.
PlaygroundPage.current.needsIndefiniteExecution = true
// Generic Result enum useful for returning values OR an error from
// asynchronous functions.
enum Result<T> {
case failure(Error)
case success(T)
}
// Custom Errors to be returned when something goes wrong.
enum NetworkError: Error {
case couldNotCreateURL(for: String)
case didNotReceiveData
}
// Perform network request asynchronous returning the result via a
// completion closure called on the main thread.
//
// In really life the result type will not be a String, it will
// probably be an array of custom structs or similar.
func performNetworkRequest(completion: #escaping (Result<String>)->Void ) {
let endpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
guard let url = URL(string: endpoint) else {
let error = NetworkError.couldNotCreateURL(for: endpoint)
completion(Result.failure(error))
return
}
let urlRequest = URLRequest(url: url)
let session = URLSession.shared
let task = session.dataTask(with: urlRequest) { (data, response, error) -> Void in
// This closure is still executing on a background thread so
// don't touch anything related to the UI.
//
// Remember to dispatch back to the main thread when calling
// the completion closure.
guard error == nil else {
// Call the completion handler on the main thread.
DispatchQueue.main.async {
completion(Result.failure(error!))
}
return
}
guard let responseData = data else {
// Call the completion handler on the main thread.
DispatchQueue.main.async {
completion(Result.failure(NetworkError.didNotReceiveData))
}
return
}
// Parse response here...
// Call the completion handler on the main thread.
DispatchQueue.main.async {
completion(Result.success("Sucessfully parsed results"))
}
}
task.resume()
}
performNetworkRequest(completion: { result in
// The generic Result type makes handling the success and error
// cases really nice by just using a switch statement.
switch result {
case .failure(let error):
print(error)
case .success(let parsedResponse):
print(parsedResponse)
}
})
Why you dont use this Library Alamofire is an HTTP networking library written in Swift.
Add this line to your Podfile
pod 'Alamofire', '~> 4.4'
Then, run the following command:
pod install
Then in your ViewController file:
import Alamofire
Alamofire.request("https://jsonplaceholder.typicode.com/todos/1").responseJSON { response in
print("Request: \(String(describing: response.request))") // original url request
print("Response: \(String(describing: response.response))") // http url response
print("Result: \(response.result)") // response serialization result
if let json = response.result.value {
print("JSON: \(json)") // serialized json response
}
If let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
print("Data: \(utf8Text)") // original server data as UTF8 string
}
}
And in here are an example of how to parse the result.
https://github.com/CristianCardosoA/JSONParser
For more info about Alamofire:
https://github.com/Alamofire/Alamofire
I hope this help.

How to get data by URL in swift? [duplicate]

var arrayData: [String] = []
let bodyData = "parameter=test"
let URL: NSURL = NSURL(string: "Link to php file")
let request:NSMutableURLRequest = NSMutableURLRequest(URL:URL)
request.HTTPMethod = "POST"
request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue())
{
(response, data, error) in
var output = NSString(data: data, encoding: NSUTF8StringEncoding)
self.arrayData = self.JSONParseArray(output)
println(self.arrayData) //Print the result
}
println(self.arrayData) //Print nothing
It look like the new value is only available in sendAsynchronousRequest
Is there a way to make the new value accessible out of the sendAsynchronousRequest ?
sendAsynchronousRequest is, as the name suggests, asynchronous. So your final println runs before the request is complete. After the completion handler runs, the new data will be available for all readers (in or outside this handler).
sendAsynchronousRequest requires a callback passed as argument, which is executed once the asynchronous request has been completed. That's out of the linear flow in your code.
This is what happens:
you call sendAsynchronousRequest
the request is started, but the function returns immediately (it doesn't wait for the request to be completed)
the println line is executed next (last line in your code)
some time later, the asynchronous request is completed, and the closure is executed
the closure assigns a value to self.arrayData
If you want to use the updated value, you have to change the logic a little bit. You can call another closure or a class method (meaning an instance method, not a static one) to post process that variable once it has been set.