4

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.

update : relevant question posted: Swift Alamofire + Promise catching

Community
  • 1
  • 1
Cmag
  • 14,946
  • 25
  • 89
  • 140
  • 2
    The request completes asynchronously, so you can't `return` a value. The caller to `Login` (which should be `login` - Capital letters for classes/structs. Small letters for instances, variables, functions) needs to pass a completion handler and then you invoke *that* completion handler from the Alamofire handler – Paulw11 Aug 16 '16 at 04:05
  • 1
    Also, in Swift, methods that can "return" an error should `throw` that error, so `login` should either invoke the passed completion handler, providing the `token` or throw an error – Paulw11 Aug 16 '16 at 04:07
  • certainly, call the callback once the network call comes back, etc. I've modified my question. – Cmag Aug 16 '16 at 04:27
  • @Paulw11 i've read up since last night, found this article: https://appventure.me/2015/06/19/swift-try-catch-asynchronous-closures/ Would you mind glancing at its contents, and making a recommendation? Does seem like the right approach to solve the problem within the method that calls `Alamofire`, correct? – Cmag Aug 16 '16 at 17:18
  • @Paulw11 I am more leaning towards `promisifying` this whole thing. Need to read up on Promises in swift... Which library is the most used ? – Cmag Aug 16 '16 at 17:24

1 Answers1

3

You can't use the return value of your Login method 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 can use Login like this:

myLib.Login(email, password) { success, message, token in
    if success {
        ...
    }
}
Yann Bodson
  • 1,634
  • 1
  • 17
  • 29
  • great, works like a charm. now I have to figure out how to bring the stuff into its own library. – Cmag Aug 16 '16 at 04:52