Vlad Dinu
Vlad Dinu

Reputation: 192

Using a value from Alamofire request outside the function

I can't seem to figure this one out: I am trying to get an error from my server (in JSON) using an Alamofire request, but I cannot get the value outside the function.

This is the implementation of my function:

    func alamoRequest(username : String, email: String, password: String, facebook: String, completionHandler : (String?) -> Void) {

    var jsonValue : JSON?
    let URL = "http://someurl.com/login.php"
    let requestParameters = ["username" : username, "email" : email, "password" : password, "facebook" : facebook, "date": NSNull()];
    var jsonString : String = String()

    Alamofire.request(.GET, URL, parameters: requestParameters).validate().responseJSON {
        response in
        switch response.result {
        case .Success:
            if let value = response.result.value {
                jsonValue = JSON(value)
                jsonString = jsonValue!["error"].stringValue
                print("Value in implementation is: \(jsonString)")

            }
        case .Failure(let error):
            print(error)
        }
    }
    completionHandler(jsonString)
}

And here is how I call it:

logic.alamoRequest(usernameField.text!, email: emailField.text!, password: passwordField.text!, facebook: "false") {
            json in
            jsonString = json!
      }

        alamoRequest = jsonString
        print("Value in call is: \(alamoRequest))

But the output is always like this:

Value from call is: Value in implementation is: Email already exists

How can I "return" something from the function or wait for it to execute without blocking the UI?

Upvotes: 2

Views: 2050

Answers (1)

Victor Sigler
Victor Sigler

Reputation: 23449

Your operation is async, the way of waiting for the completion is just using closures, you're trying to return the value outside the closure so the value is not retrieved yet!! and it's the cause of your empty value, you can "throw" the completionHandler inside your closure like in the following example :

func alamoRequest(username : String, email: String, password: String, facebook: String, completionHandler : (String?) -> Void) {

   var jsonValue : JSON?
   let URL = "http://someurl.com/login.php"
   let requestParameters = ["username" : username, "email" : email, "password" : password, "facebook" : facebook, "date": NSNull()];
   var jsonString : String = String()

   Alamofire.request(.GET, URL, parameters: requestParameters).validate().responseJSON {
    response in
       switch response.result {
       case .Success:
           if let value = response.result.value {
               jsonValue = JSON(value)
               jsonString = jsonValue!["error"].stringValue
               print("Value in implementation is: \(jsonString)")
               completionHandler(jsonString)
           }
       case .Failure(let error):
           print(error)
       }
   } 
}

Nevertheless, in the above way the only way of you don't know if exist some error in the request because your completionHandler is called only inside the .Success case, if you want you can call it always, after the last case of the enum.

Alamofire.request(.GET, URL, parameters: requestParameters).validate().responseJSON {
    response in
       switch response.result {
       case .Success:
           if let value = response.result.value {
               jsonValue = JSON(value)
               jsonString = jsonValue!["error"].stringValue
               print("Value in implementation is: \(jsonString)")

           }
       case .Failure(let error):
           jsonString = nil
       }
       completionHandler(jsonString)
   } 
}

And if the jsonString is nil some error has ocurred, but again you don't know anything about the error, then you have two options :

  1. Change your closure to always return the jsonString and the error too
  2. Encapsulating the error into a throwable closure.

The first case is very simple, just change your closure and always have the error returned, if it's nil then no error ocurred.

The other option I think is the better, like in the following example:

func alamoRequest(username : String, email: String, password: String, facebook: String, 
                  completion: (inner: () throws -> String) -> ()) {

   var jsonValue : JSON?
   let URL = "http://someurl.com/login.php"
   let requestParameters = ["username" : username, "email" : email, "password" : password, "facebook" : facebook, "date": NSNull()];
   var jsonString : String = String()

   Alamofire.request(.GET, URL, parameters: requestParameters).validate().responseJSON {
    response in
       switch response.result {
       case .Success:
           if let value = response.result.value {
               jsonValue = JSON(value)
               jsonString = jsonValue!["error"].stringValue
               print("Value in implementation is: \(jsonString)")
               completionHandler(inner: { return jsonString })
           }
       case .Failure(let error):
           completionHandler(inner: { return error })
       }
   } 
}

And then you can call it like in the following way:

self.alamoRequest(usernameField.text!, email: emailField.text!, password: passwordField.text!, facebook: "false") { (inner: () throws -> String) -> Void in
  do {
    let result = try inner()
  } catch let error {
     print(error)
  }
}

The trick is that the alamoRequest function takes an additional closure called 'inner' of the type () throws -> String. This closure will either provide the result of the computation, or it will throw. The closure itself is being constructed during the computation by one of two means:

  • In case of an error: inner: {throw error}
  • In case of success: inner: {return result}

I strongly recommend you an excellent article about using try/catch in async calls Using try / catch in Swift with asynchronous closures

I hope this help you.

Upvotes: 6

Related Questions