Reputation: 3828
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)¤cy=\(currency)&language=\(language)®ion=\(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
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)¤cy=\(currency)&language=\(language)®ion=\(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