0

Possibly this could be a misunderstanding that I have in Swift. I'm new to the language and my background is in Perl, which makes me feel like swift is acting differently.

I have 2 files. ViewController.swift and APICalls.swift. In viewcontroller I have a function for a button. In this function I'm making a call to another function in the APICalls. When I do, my println() in the APICalls is printing the correct data, however, it's doing so after my println() in the button function.

Viewcontroller.swift

@IBAction func buttonStuff(sender: AnyObject) {

        var api = APICalls()
        var token:String

        token = api.TEST("letmein")

        println("\ntokenDidLOAD = \(token)\n")

    }

APICalls.swift

class APICalls {

    func TEST(command: String) -> (String) {

        var token:String = ""

        // Form URL-Encoded Body
        let bodyParameters = [
            "COMMAND":command,
        ]

        let encoding = Alamofire.ParameterEncoding.URL

        // Fetch Request
       Alamofire.request(.POST, "http://api.com/?v=1", parameters: bodyParameters, encoding: encoding)
            .validate(statusCode: 200..<300)
            .responseJSON{(request, response, data, error) in

                if (error == nil)
                {
                    let json = JSON(data!)
                    token = json["TOKEN"].string!
                    println("jsonAPI = \(json) \n\n")
                }
                else
                {
                    println("HTTP HTTP Request failed: \(error)")
                }

            }

        return token

     }
}

Here is my output

tokenDidLOAD = 

jsonAPI = {
  "STATUS" : "OK",
  "TOKEN" : "698798765432134654",
} 

I don't understand why 'tokenDidLOAD' is printing first before the jsonAPI.

bdizzle
  • 419
  • 2
  • 5
  • 19
  • [Difference between synchronous and asynchronous](http://stackoverflow.com/q/21122842/468724) – Inder Kumar Rathore Apr 17 '15 at 05:40
  • You should read the "Response Handling" section in the Alamofire Readme. It explicitly mentions the asynchronous procedure. – Martin R Apr 17 '15 at 05:45
  • Similar questions with answers: http://stackoverflow.com/questions/29377824/swift-alamofire-return-value-is-empty, http://stackoverflow.com/questions/29377824/swift-alamofire-return-value-is-empty, http://stackoverflow.com/questions/25141829/swift-closure-with-alamofire (and some more ...) – Martin R Apr 17 '15 at 05:47

3 Answers3

1

Because the request that you make is asynchronous. You first return the token that is not present yet and only after that the request is actually finished. You don't need to get the token as TEST function's return value. Your TEST should be like this:

func TEST(command: String, completion:(String)->()) {

var token:String = ""

// Form URL-Encoded Body
let bodyParameters = [
    "COMMAND":command,
]

let encoding = Alamofire.ParameterEncoding.URL

// Fetch Request
Alamofire.request(.POST, "http://api.com/?v=1", parameters: bodyParameters, encoding: encoding)
    .validate(statusCode: 200..<300)
    .responseJSON{(request, response, data, error) in

        if (error == nil)
        {
            let json = JSON(data!)
            token = json["TOKEN"].string!
            println("jsonAPI = \(json) \n\n")
            completion(token)
        }
        else
        {
            println("HTTP HTTP Request failed: \(error)")
        }

}

Then you call it:

api.TEST("letmein", {(newToken : String) in 
                       token = newToken
                        println("\ntokenDidLOAD = \(token)\n")
                     })
Andrey Chernukha
  • 21,488
  • 17
  • 97
  • 161
0

Because this println("jsonAPI = \(json) \n\n") is in side the block statement, which will run as soon request get complete, till then compiler will run next jobs means your println("\ntokenDidLOAD = \(token)\n"). Block will Run on another subthread.

If you want to print println("\ntokenDidLOAD = \(token)\n") when request get complete, then use oncomplete based block.

May this help you bit a way.

Viral Savaj
  • 3,379
  • 1
  • 26
  • 39
0

Alamofire fetches data asynchronously by default, which is the desired behaviour for pretty much every website or app. By the time the function reaches the return token the request might not have been finished yet.

One idea is to pass in a closure to your API call to be executed once the request is finished:

func TEST(command: String, closure: (JSON) -> ()) -> (String) {

    var token:String = ""

    // Form URL-Encoded Body
    let bodyParameters = [
        "COMMAND":command,
    ]

    let encoding = Alamofire.ParameterEncoding.URL

    // Fetch Request
   Alamofire.request(.POST, "http://api.com/?v=1", parameters: bodyParameters, encoding: encoding)
        .validate(statusCode: 200..<300)
        .responseJSON{(request, response, data, error) in

            if (error == nil)
            {
                let json = JSON(data!)
                token = json["TOKEN"].string!
                println("jsonAPI = \(json) \n\n")
                closure(json)
            }
            else
            {
                println("HTTP HTTP Request failed: \(error)")
            }

        }

    return token

 }

Then you could call it via:

api.TEST("letmein") { json in
    let token = json["TOKEN"].stringValue
    println("\ntokenDidLOAD = \(token)\n")
}

The closure accepts an object of type JSON as a parameter, which you can use to do whatever you want with the json inside.

Also one thing to note: I'm assuming you're using the SwiftyJSON library. Before, you called json["TOKEN"].string!. This force-unwraps an optional, which should be avoided generally (unless you're absolutely sure it has a value). In such a case, you would want to use json["TOKEN"].stringValue instead. It returns String instead of String?

padarom
  • 3,529
  • 5
  • 33
  • 57