I'm developing an iOS Application in Swift that uses Google Cloud Endpoints as its backend. For now, I've just written the code for the user to login with his Gmail account and I've refactored it as much as possible but the callback chain is making me nervous.
The reason I'm worried is because firstly, I'm fairly new to programming, and secondly, the company where I work now uses a similar callback-chain method to make network calls and I sort of dislike it because it takes a while for a new developer to figure out the sequence of events and it feels a bit like spaghetti code.
I would appreciate any suggestions for improving the code especially to avoid callback chaining or at least suggestions to make it more navigable.
P.S.: If you're wondering why I'm rebuilding the OAuth 2 authentication object from NSUserDefaults, please see this question.
class ViewController: UIViewController{
//Member variables
//Outlets
@IBOutlet weak var loginButton: UIButton!
@IBOutlet weak var detailsLabel: UILabel!
//Lifecycle Overrides
override func viewDidLoad() {
if let auth : GTMOAuth2Authentication = GoogleEndpointAssistant.rebuildGTMOAuth2AuthenticationFromUserDefaults(){
setAuthentication(auth)
}
}
//Methods
func setAuthentication(auth : GTMOAuth2Authentication){
//set OAuth2 as Google End Point service authorizer
ASSession.sharedSession.gtlService.authorizer = auth;
//save user's ID (Will be used to fetch user's information from server)
ASSession.sharedSession.user.identifier = NSNumber(longLong: (auth.userID as NSString).longLongValue);
configureSession()
}
/**
Configures the session instance (ASSession.configure()) and, if successsful, calls identifyUser()
*/
func configureSession(){
MBProgressHUD.showHUDAddedTo(self.view, animated: true)
ASSession.sharedSession.configure({ (error:NSError?) -> Void in
if error == nil {
self.identifyUser()
}else{
self.detailsLabel.text = error!.localizedDescription;
MBProgressHUD.hideAllHUDsForView(self.view, animated: true)
ErrorMessage.SessionSettingsDownloadFailed.alertView().show()
}
})
}
/**
Checks whether or not the user is registered on the server.
If the user is registered, a segue is peformed to the dashboard screen.
If the user is not registered, a segue is performed to the complete registration screen.
*/
func identifyUser(){
let userExistsQuery : GTLQueryHdys = GTLQueryHdys.queryForUserGetUserWithIdentifier(ASSession.sharedSession.user.identifier.longLongValue) as GTLQueryHdys;
ASSession.sharedSession.gtlService.executeQuery(userExistsQuery, completionHandler: { (ticket:GTLServiceTicket!, response:AnyObject!, error:NSError!) -> Void in
MBProgressHUD.hideAllHUDsForView(self.view, animated: true)
if response != nil {
//user is registered on the server and details have been received.
NSLog("User registered")
self.loginButton.setTitle("Registered", forState: UIControlState.Normal)
}else if GoogleEndpointAssistant.statusCodeFromError(error) == 404 {
////user is not registered on the server.
NSLog("User not Registered")
let auth = ASSession.sharedSession.gtlService.authorizer as GTMOAuth2Authentication;
self.detailsLabel.text = "Expiry: \(auth.expirationDate), Auth: \(auth.canAuthorize)."
self.loginButton.setTitle("Not Registered", forState: UIControlState.Normal)
}else{
//an error occurred while request was in progress.
NSLog("Error: \(error.localizedDescription)")
self.detailsLabel.text = error.localizedDescription;
self.loginButton.setTitle("Error", forState: UIControlState.Normal)
}
})
}
//Actions
/**
Displays GTMOAuth2ViewControllerTouch so that the user can sign-in with his/her gmail account.
Once complete, finishedWithAuth is invoked
*/
@IBAction func authenticateUser(){
let authenticationViewController = GTMOAuth2ViewControllerTouch(
scope: kGTLAuthScopeHdysUserinfoEmail,
clientID: GoogleCloudEndPointClientID,
clientSecret: GoogleCloudEndPointClientSecret,
keychainItemName: GoogleCloudKeychainItemName,
delegate: self,
finishedSelector: "viewController:finishedWithAuth:error:"
)
self.presentViewController(authenticationViewController, animated: true, completion: nil)
}
//Delegate Methods
/**
This callback is invoked after the user completes sign-in with his/her gmail account in the modal GMTOAuth2ViewControllerTouch.
If the sign-in was successful, OAuth2Authentication is set as the Google Cloud Endpoint service authorizer
;see also; setAuthentication(auth: OAuth2Authentication)
*/
@objc(viewController:finishedWithAuth:error:)
func finishedWithAuth(viewController :GTMOAuth2ViewControllerTouch , auth:GTMOAuth2Authentication,error:NSError!){
self.dismissViewControllerAnimated(true, completion: nil);
if error != nil {
println("Authentication Failure: \(error.localizedDescription)");
ErrorMessage.OAuth2AuthenticationFailed.alertView().show()
}else{
GoogleEndpointAssistant.saveGTMOAuth2AuthenticationToUserDefaults(auth)
setAuthentication(auth)
}
}
}