Cmag Cmag - 3 months ago 18
iOS Question

swift2.0, returning information from REST calls (Alamofire)

Folks,
Trying to understand the correct programmatic approach to returning data from making external API calls.

Before I go ahead and create my own swift framework for code reuse (that manages all rest api calls for my app), I would like to ask the community about how they deal with the following situation:

Here we have a button that gets tapped on the login view, we need to make a call to our auth service, and react on things we get back.

viewController:

import myLib

@IBAction func loginButtonTapped(sender: AnyObject) {
let email = emailField.text!
let password = pwField.text!
let loginResult = myLib.login(email,password)
if (loginResult.success) {
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isUserLoggedIn")
NSUserDefaults.standardUserDefaults().synchronize()
self.dismissViewControllerAnimated(true, completion: nil)
} else {
displayAlertMessage(loginResult.message)
}
}


myLib.login:

import Foundation
import Alamofire
import SwiftyJSON

public func Login(email: String, password: String, completion: ((success: Bool, message: String?, token: String?) -> Void)) {
let parameters: [String: String] = [
"username" : email,
"password" : password
]
let endpoint = "https://api.foo.bar/login"
Alamofire.request(.POST, endpoint, parameters: parameters, encoding: .JSON)
.responseJSON { response in
guard response.result.error == nil else {
print(response.result.error!)
completion(success: false, message: "error calling POST on /account/login", token: nil)
return
}

if let value = response.result.value {
let apiResponseJSONBody = JSON(value)
completion(success: true, message: nil, token: apiResponseJSONBody["token"].string)
}
}

}



  • Is it correct to pass results back as structs? I noticed that we have to make the struct public in order to be able to return it.



Thanks! I greatly appreciate all feedback.

Answer

You can't use the return value of Login since the request is asynchronous.

Basically, your Login method will always return immediately with success = false.

To return asynchronously, you need to add a completion block to Login:

public func Login(email: String, password: String, completion: ((success: Bool, message: String?, token: String?) -> Void)) {
}

Then when you get the response from Alamofire, call your completion block like this:

completion(success: false, message: nil, token: nil)

From your view controller, you use Login like this:

myLib.Login(email, password) { success, message, token in
    if success {
        ...
    }
}