MattBlack
MattBlack

Reputation: 3828

Swift execute functions sequentially

I have the below code which obtains the access token from a facebook login and returns it as 'accessToken'. Once I have the access token I wish to pass this to my server in a request and return the response as an array.

The issue I have is that the request to the server executes before the accessToken is obtained. I have looked into closure statements but I cannot see a way where I can order the execution of the functions without ending up nesting. I don't mind nesting in this instance but in future if I have say 5 functions this will begin to look messy.

Am I approaching this in the best way by using classes and functions? Usually when I code in swift all the code relevant to the viewController would be contained in 1 file, but as the project gets larger I am looking to implement a more OOP approach to make the project more manageable. How would I best achieve this?

import Foundation
import UIKit

class registrationPage: UIViewController {

    @IBAction func facebookButton(_ sender: Any) {

        // Get the access token from facebook
        let accessToken = facebookLogin().login()

        // Get the users currency, langage and locale.
        let currency = Locale.current.currencyCode ?? "GBP"
        let language = Locale.current.languageCode ?? "GB"
        let region = Locale.current.regionCode ?? "GB"

        let params = "accessToken=\(accessToken)&currency=\(currency)&language=\(language)&region=\(region)"

        let resultArray = database().connect(endPoint: "loginfb.php?", params: "\(params)")

        print(resultArray)

    }
}

class facebookLogin {

    var response = ""

    func login(completion: (_ result: String) -> Void) {

     let loginManager = LoginManager()

     loginManager.logIn(readPermissions:[ReadPermission.publicProfile, ReadPermission.email], viewController: registrationPage() as UIViewController) {

    loginResult in switch loginResult {

    case .failed:
    self.response = "ERROR"

    case .cancelled:
    self.response = "ERROR"

    case .success:

        self.response = "\(String(describing: FBSDKAccessToken.current().tokenString!))"
        print(self.response)

    }
        }

        completion(self.response)
    }
}

Upvotes: 0

Views: 1224

Answers (1)

Fabian
Fabian

Reputation: 5358

loginManager.logIn is asynchronous, thats why it takes a closure. You can synchronize the call or as you said use nested closures where one calls the next.

To make let accessToken = facebookLogin().login() synchronous with DispatchGroup:

class facebookLogin {
    func login() -> String {
        let loginManager = LoginManager()

        var response = ""

        let group = DispatchGroup()
        group.enter() // loginManager.logIn

        loginManager.logIn(readPermissions:[ReadPermission.publicProfile, ReadPermission.email], viewController: registrationPage() as UIViewController) {

            loginResult in switch loginResult {                
            case .failed:
                self.response = "ERROR"

            case .cancelled:
                self.response = "ERROR"

            case .success:            
                self.response = "\(String(describing: FBSDKAccessToken.current().tokenString!))"
                print(self.response)

            }

            group.leave() // loginManager.logIn
        }

        group.wait()
        return response
    }
}

If you don't like the facebookLogin().login() { accessToken in ... } syntax, you could put the { accessToken in ... } part into its own function

func callServer(accessToken: String) {
    // Get the users currency, langage and locale.
    let currency = Locale.current.currencyCode ?? "GBP"
    let language = Locale.current.languageCode ?? "GB"
    let region = Locale.current.regionCode ?? "GB"

    let params = "accessToken=\(accessToken)&currency=\(currency)&language=\(language)&region=\(region)"

    let resultArray = database().connect(endPoint: "loginfb.php?", params: "\(params)")

    print(resultArray)
}

and call it with

@IBAction func facebookButton(_ sender: Any) {

    // Get the access token from facebook
    facebookLogin().login(completion: callServer(accessToken:))
}

Upvotes: 1

Related Questions