Reputation: 192
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
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 :
jsonString
and the error
tooThe 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:
inner: {throw error}
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