Jiacai Lau Jiacai Lau - 4 months ago 58
Swift Question

Swift: I am unable to pass back a variable from Firebase FIRAuth function to a declared variable from a parent funciton

I am a newb in swift and I've been trying to learn myself. I come across a problem which I am unsure on how to fix it. The code is as follow:

import Foundation
import Firebase

class UserLogin {

var email:String?
var password:String?

init(email:String, password:String){
self.email = email
self.password = password
}

func userLogin() -> String{
var errorMsg:String = ""
//Email & Password Integrity check
if (email == ""){
errorMsg = "Please enter your email"
} else if (email?.rangeOfString("@") == nil || email?.rangeOfString(".") == nil){
errorMsg = "Email is invalid"
}else if (password == ""){
errorMsg = "Please enter your password"
} else if (password?.characters.count < 8){
errorMsg = "Password is invalid"
}else{
print("Logging In... with Email:\(email!) and Password:\(password!)")
//Firebase Authentication Process"

FIRAuth.auth()?.signInWithEmail(email!, password: password!){ (user, error) in
// ...
if (error != nil){
let errorCode = error!.code
if (errorCode == 17009){
errorMsg = "You have entered the wrong password"
} else if (errorCode == 17011){
errorMsg = "Your email does not exist"
} else if (errorCode == 17010) {
errorMsg = "You have tried to login too many times with the wrong credentials. Please try again later."
} else {
print(error)
}
} else {
print("User is Logged In")
errorMsg = "You have successfully Logged In"
}
}
}
return errorMsg
}

}


Basically in my ViewController, I have a separate code that works like this

let alert = UIAlertController(title: "Error", message: errorMsg, preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default, handler: nil)
alert.addAction(action)
self.presentViewController(alert, animated: true, completion: nil)


This works fine for all my errorMsg within my userLogin func but for the few errorMsgs that are generated based on the error.code provided by FIRAuth, it does not appear.

I read around and figured it might be because FIRAuth is an Asynchronous Calls but I do not know how to work around it.

Sorry if this sounds really stupid but I've been figuring it out for the whole day but to no avail and it would be awesome to get some help from you guys.

Addition:
I implemented the CompletionHandler as recommended but I don't understand why it did not work although it should... following is my code.


UserLogin1.swift

import Foundation
import Firebase

class UserLogin1 {

var email:String?
var password:String?

init(email:String, password:String){

self.email = email
self.password = password
}

func userLogin(completion:(message:String)->()) {

var errorMsg:String = ""
//Email & Password Integrity check
if (email == ""){

errorMsg = "Please enter your email"

} else if (email?.rangeOfString("@") == nil || email?.rangeOfString(".") == nil){

errorMsg = "Email is invalid"

}else if (password == ""){

errorMsg = "Please enter your password"

} else if (password?.characters.count < 8){

errorMsg = "Password is invalid"

}else if (errorMsg != ""){

completion(message: errorMsg)

}else{

print("Logging In... with Email:\(email!) and Password:\(password!)")
//Firebase Authentication Process"

FIRAuth.auth()?.signInWithEmail(email!, password: password!){ (user, error) in
// ...
if (error != nil){

let errorCode = error!.code

if (errorCode == 17009){

errorMsg = "You have entered the wrong password"

} else if (errorCode == 17011){

errorMsg = "Your email does not exist"

} else if (errorCode == 17010) {

errorMsg = "You have tried to login too many times with the wrong credentials. Please try again later."

} else {

print(error)

}

} else {
print("User is Logged In")
errorMsg = "You have successfully Logged In"
}


}
completion(message: errorMsg)

}

}

}


LoginViewController

import UIKit

class LoginViewController: UIViewController {

//Properties
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var emailTextField: UITextField!

//Actions
@IBAction func loginButton(sender: AnyObject) {

let email = self.emailTextField.text!
let password = self.passwordTextField.text!

let user = UserLogin1(email: email, password: password)

user.userLogin(){ (message:String) in
print(message)
}


}

Answer

From my comment, I mentioned that you will have two possible solutions. The best choice will depends on what exactly you are trying to achieve. But I'm sure you can make it work with any of them.

With Completion Handler

func userLogin(completion:(message:String)->()){
    var errorMsg:String = ""
    if (email == ""){
    ...
    //check if found any errors yet
    }else if (errorMsg != ""){
        completion(errorMsg)
    } else {
        FIRAuth.auth()?.signInWithEmail(email!, password: password!){ (user, error) in
            if (error != nil){
            ...
            } else {
                errorMsg = "You have successfully Logged In"
            }
            completion(errorMsg)
        }
    }
}

userLogin(){ (message:String) in
    // this will only be called when userLogin trigger completion(errorMsg)...
    print(message)
}

With Self

func userLogin() -> Void{
    var errorMsg:String = ""
    if (email == ""){
    ...
    //check if found any error yet
    }else if (errorMsg != ""){
        self.errorMsg = errorMsg
    } else {
        FIRAuth.auth()?.signInWithEmail(email!, password: password!){ (user, error) in
            if (error != nil){
            ...
            } else {
                errorMsg = "You have successfully Logged In"
            }
            self.errorMsg = errorMsg
        }
    }
}