How to catch ERROR_EMAIL_ALREADY_IN_USE in FirebaseAuth in Swift? - ios

I'm trying to authenticate new users and storing their email and password into FirebaseAuth using the following code:
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
if user == nil || error != nil {
let alert = UIAlertController(title: "Please Try Again",
message: "Invalid email or password",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default) {_ in })
self.present(alert, animated: true)
}
else {
self.performSegue(withIdentifier: "nextpage", sender: self)
}
}
However, I'm running into a problem, which is that when a user enters an email is already in use, FirebaseAuth doesn't return an error; instead, it simply overrides the current email. I read in the documentation that an error called ERROR_EMAIL_ALREADY_IN_USE can be thrown as part of FirebaseAuthUserCollisionException, how can this be caught? Thanks!

Place this code in the callback handler:
if let error = error {
switch FIRAuthErrorCode(rawValue: error!._code) {
case .emailAlreadyInUse:
print("Email already in use")
default:
print("Other error")
}
}
Now you can remove the if user == nil || error != nil if block.
Also see https://firebase.google.com/docs/reference/ios/firebaseauth/api/reference/Enums/FIRAuthErrorCode for a list of error codes.

April 2019
{ (error) in
switch AuthErrorCode(rawValue: error._code) {
case .emailAlreadyInUse?:
print("Email is already in use")
default:
print("Other error types.")
}
}

Related

Handling Errors in New Firebase and Swift

I'm trying to add error handling in creating user button in iOS project using swift and firebase:
Here's the code for the button:
#IBAction func Register(sender: AnyObject) {
if NameTF.text == "" || EmailTF.text == "" || PasswordTF.text == "" || RePasswordTF == "" || PhoneTF.text == "" || CityTF.text == ""
{
let alert = UIAlertController(title: "عذرًا", message:"يجب عليك ملىء كل الحقول المطلوبة", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
} else {
if PasswordTF.text != RePasswordTF.text {
let alert = UIAlertController(title: "عذرًا", message:"كلمتي المرور غير متطابقتين", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
} else {
FIRAuth.auth()?.createUserWithEmail(EmailTF.text!, password: PasswordTF.text!, completion: { user, error in
print(error)
if error != nil {
let errorCode = FIRAuthErrorNameKey
switch errorCode {
case "FIRAuthErrorCodeEmailAlreadyInUse":
let alert = UIAlertController(title: "عذرًا", message:"الإيميل مستخدم", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
case "FIRAuthErrorCodeUserNotFound":
let alert = UIAlertController(title: "عذرًا", message:"المستخدم غير موجود", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
case "FIRAuthErrorCodeInvalidEmail":
let alert = UIAlertController(title: "عذرًا", message:"الإيميل غير صحيح", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
case "FIRAuthErrorCodeNetworkError":
let alert = UIAlertController(title: "عذرًا", message:"خطأ في الاتصال بالانترنت", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
default:
let alert = UIAlertController(title: "عذرًا", message:"خطأ غير معروف", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
}
} else {
FIRAuth.auth()?.signInWithEmail(self.EmailTF.text!, password: self.PasswordTF.text!, completion: { (user: FIRUser?, error: NSError?) in
if let error = error {
print(error.localizedDescription)
} else {
self.ref.child("UserProfile").child(user!.uid).setValue([
"email": self.EmailTF.text!,
"name" : self.NameTF.text!,
"phone": self.PhoneTF.text!,
"city" : self.CityTF.text!,
])
print("Sucess")
// self.performSegueWithIdentifier("SignUp", sender: nil)
}
})
} //else
})
} //Big else
} //Big Big else
}
}//end of
I'm not sure if the syntax of the errors in switch statement is correct or not!
Because when I tested it in the simulator it always gives me the defualt case which is unknown error!
+
I could not find the syntax in the documentation:
https://firebase.google.com/docs/auth/ios/errors
So, What's the correct syntax to add error handling using new firebase and swift!
I've actually just struggled with this for quite a bit of time and found what the issue was. I've tried the code posted in an answer above and the error.code line gave me an error. It did work with error._code though. In other words, credit for the original answer to Paul with a slight modificaiton. Here's my final code (I will edit it for all errors though):
if let errCode = AuthErrorCode(rawValue: error!._code) {
switch errCode {
case .errorCodeInvalidEmail:
print("invalid email")
case .errorCodeEmailAlreadyInUse:
print("in use")
default:
print("Create User Error: \(error)")
}
}
Updated for Swift 4 + Firebase 4 + UIAlertController
extension AuthErrorCode {
var errorMessage: String {
switch self {
case .emailAlreadyInUse:
return "The email is already in use with another account"
case .userNotFound:
return "Account not found for the specified user. Please check and try again"
case .userDisabled:
return "Your account has been disabled. Please contact support."
case .invalidEmail, .invalidSender, .invalidRecipientEmail:
return "Please enter a valid email"
case .networkError:
return "Network error. Please try again."
case .weakPassword:
return "Your password is too weak. The password must be 6 characters long or more."
case .wrongPassword:
return "Your password is incorrect. Please try again or use 'Forgot password' to reset your password"
default:
return "Unknown error occurred"
}
}
}
extension UIViewController{
func handleError(_ error: Error) {
if let errorCode = AuthErrorCode(rawValue: error._code) {
print(errorCode.errorMessage)
let alert = UIAlertController(title: "Error", message: errorCode.errorMessage, preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
alert.addAction(okAction)
self.present(alert, animated: true, completion: nil)
}
}
}
Usage example:
Auth.auth().signIn(withEmail: email, password: password, completion: { (user, error) in
if error != nil {
print(error!._code)
self.handleError(error!) // use the handleError method
return
}
//successfully logged in the user
})
Even though this has been answered correctly, wanted to share a nice implementation for this we added to our project.
This can be done for other error types as well, but we just needed it for the FIRAuthErrorCodes.
If you extend FIRAuthErrorCode to have a variable errorMessage of type string, you can have your own error messages for the users:
extension FIRAuthErrorCode {
var errorMessage: String {
switch self {
case .errorCodeEmailAlreadyInUse:
return "The email is already in use with another account"
case .errorCodeUserDisabled:
return "Your account has been disabled. Please contact support."
case .errorCodeInvalidEmail, .errorCodeInvalidSender, .errorCodeInvalidRecipientEmail:
return "Please enter a valid email"
case .errorCodeNetworkError:
return "Network error. Please try again."
case .errorCodeWeakPassword:
return "Your password is too weak"
default:
return "Unknown error occurred"
}
}
}
You could customize only some as we have above and group the rest under "Unknown error".
With this extension you can handle an error as shown in Vladimir Romanov's answer:
func handleError(_ error: Error) {
if let errorCode = FIRAuthErrorCode(rawValue: error._code) {
// now you can use the .errorMessage var to get your custom error message
print(errorCode.errorMessage)
}
}
FIRAuthErrorCode is an int enum not a string enum. Do the following:
if let error = error {
switch FIRAuthErrorCode(rawValue: error.code) !{
case .ErrorCodeInvalidEmail:
More info in this answer.
I am using Swift 5 and Firebase 6.4.0 and for me, none of the above really worked. After trying around a bit I came up with this:
Auth.auth().createUser(withEmail: emailTextfield.text!, password: passwordTextfield.text!) { (user, error) in
if error!= nil{
let alert = UIAlertController(title: "Error", message: error!.localizedDescription, preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
alert.addAction(okAction)
self.present(alert,animated: true)
}

How to verify a user's email address on iOS with Firebase?

I'm stuck with email verification with firebase. I've looked around for guidance but no help. After the user verifies his email, my code still prints out the user has not been verified. I'm still trying to get used to the syntax of firebase. Here is my code:
if FIRAuth.auth()?.currentUser!.emailVerified == true{
FIRAuth.auth()?.signInWithEmail(email.text!, password: passsword.text!, completion: {
user, error in
if error != nil{
print("Email/password is wrong or user does not exist")
}else{
print("Successful login.")
}
})
}else{
print("Please verify your email.")
}
here is my code for the sign up section:
let eduEmail = email.text
let endInEdu = eduEmail?.hasSuffix("my.utsa.edu")
if endInEdu == true {
FIRAuth.auth()?.createUserWithEmail(email.text!, password: passsword.text!, completion: {
user, error in
if error != nil{
let alert = UIAlertController(title: "User exists.", message: "Please use another email or sign in.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
print("Email has been used, try a different one")
}else{
FIRAuth.auth()?.currentUser!.sendEmailVerificationWithCompletion({ (error) in
})
let alert = UIAlertController(title: "Account Created", message: "Please verify your email by confirming the sent link.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
print("This is a college email and user is created")
}
})
}else{
print("This is not a my.utsa.edu email")
}
You checked if the user email was verified even before signing them in.
This is what worked for me.
FIRAuth.auth()?.signInWithEmail(txtUsername.text!, password: txtPassword.text!) {
(user, error) in
if let user = FIRAuth.auth()?.currentUser {
if !user.emailVerified{
let alertVC = UIAlertController(title: "Error", message: "Sorry. Your email address has not yet been verified. Do you want us to send another verification email to \(self.txtUsername.text).", preferredStyle: .Alert)
let alertActionOkay = UIAlertAction(title: "Okay", style: .Default) {
(_) in
user.sendEmailVerificationWithCompletion(nil)
}
let alertActionCancel = UIAlertAction(title: "Cancel", style: .Default, handler: nil)
alertVC.addAction(alertActionOkay)
alertVC.addAction(alertActionCancel)
self.presentViewController(alertVC, animated: true, completion: nil)
} else {
print ("Email verified. Signing in...")
}
}
}
You are using the coalescing operator after FIRAuth.auth() which means the following method call will return nil when FIRAuth.auth() was nil. If this is the case, your comparison with true will fail, since nil is not true.
I suggest you to refactor your code like this for easier debugging:
guard let auth = FIRAuth.auth(), user = auth.currentUser else {
print("No auth / user")
}
guard user.emailVerified else {
print("Email not verified")
return
}
guard let email = email.text, password = passsword.text else {
print("No email or password")
return
}
auth.signInWithEmail(email, password: password) { user, error in
if let error = error {
print("Email/password is wrong or user does not exist, error: \(error)")
} else {
print("Successful login.")
}
}
You should find your error easier like this.

Firebase Authentication:Newly created user can't be saved in Firebase

I was registering users with Firebase, but newly created user can't be saved in Firebase though I've refreshed the Firebase Login & Auth tab.Could you help me fix it?
The code is here:
let saveAction = UIAlertAction(title: "Save", style: .Default) { (action:UIAlertAction) in
self.ref.createUser(self.textFieldLoginEmail.text, password: self.textFieldLoginPassword.text, withCompletionBlock: { (error:NSError!) in
if error == nil
{
self.ref.authUser(self.textFieldLoginEmail.text, password: self.textFieldLoginPassword.text, withCompletionBlock: { (error, auth) -> Void in
})
}
})
}
Try an figure out what is the error is when you try and log in, here an example for the use of the Firebase error log:
Data.ref.authUser(textFieldLoginEmail.text, password: textFieldLoginPassword.text, withCompletionBlock: { error, authData in
if error != nil {
var msg = ""
if let errorCode = FAuthenticationError(rawValue: error.code) {
switch (errorCode) {
case .UserDoesNotExist:
msg = "Invalid user name"
case .InvalidEmail:
msg = "Invalid email"
case .InvalidPassword:
msg = "Invalid password"
case .NetworkError:
msg = "we're sorry, network error has occurred, please login from the settings menu later"
default:
msg = "we're sorry, we encountered an error, please try again"
}
let alert = UIAlertController(title: "User Authentication error",
message: msg,
preferredStyle: .Alert)
let OKAction = UIAlertAction(title: "OK",
style: .Default) { (action: UIAlertAction!) -> Void in
if errorCode == .NetworkError {
self.performSegueWithIdentifier("ToMenuSegue", sender: self)
}
self.textFieldLoginPassword.text = ""
self.view.endEditing(true)
}
alert.addAction(OKAction)
self.presentViewController(alert, animated: true, completion: nil)
}
} else {
// Logged in
self.performSegueWithIdentifier("ToMenuSegue", sender: self)
}
})
The full Firebase errors list

Why is the segue executing even though the wrapping conditional is not met?

I want to segue to a different view when the user login is successful.
#IBAction func loginAction(sender: UIButton) {
let email = self.email.text
let password = self.password.text
if (email != "" && password != "") { //check that password and email are entered
BASE_REF.authUser(email, password: password, withCompletionBlock: { (error, authData) -> Void in
if (error != nil) { //check if login was successful
print(error)
} else {
NSUserDefaults.standardUserDefaults().setValue(authData.uid, forKey: "uid")
print ("Logged in :)")
self.performSegueWithIdentifier("loginSuccessful", sender: nil)
self.logoutButton.hidden = false
}
})
} else { //otherwise, return error and show alert
let alert = UIAlertController(title: "Error", message: "Enter Email and Password", preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default, handler: nil)
alert.addAction(action)
self.presentViewController(alert, animated: true, completion: nil)
}
}
The segue lies in the else block. The if block checks if there was an error in logging the user in and prints an error if there was. The error is printing when an incorrect ID is passed in, however, the app still segues anyway.
Why is this happening and how can I prevent it!
Thanks?
FIXED: The problem was that my segue was between the button and the ViewController instead of between 2 ViewControllers.
Recreating the segue between the two ViewControllers solved the problem.
Credits to Arun Gupta and Rahul Katariya

Handling Email Does not Exist Error - iOS project using swift

Here's my ResetPassword Button for my iOS application using xcode and swift:
//ResetPssword Button
#IBAction func ResetPassword(sender: AnyObject) {
if validateEmail(EmailTextField.text!) == false {
print("Enter a Valid Email Address")
let VaildMessage = "Enter an Email Address"
//Empty TextField Alert Message
self.disaplayErrorMessage(VaildMessage)
}
//Reset
else {
ref.resetPasswordForUser(EmailTextField.text) { (ErrorType) -> Void in
if ErrorType != nil {
let error = ErrorType
print("There was an error processing the request \(error.description)")
let errorMessage:String = "The Email You Entered is not Exist"
//Error Alert Message
self.disaplayErrorMessage(errorMessage)
} else {
print("Password Reset Sent Successfully")
if let Email = self.EmailTextField.text {
let successMessage = "Email Message was Sent to You at \(Email)"
//Success Alert Message
self.disaplayErrorMessage(successMessage) }
}
} //reset
} //Big Else
} //Button
//Display Alert Message With Confirmation
func disaplayErrorMessage(theMessage:String)
{
//Display alert message with confirmation.
let myAlert = UIAlertController(title: "Alert", message: theMessage, preferredStyle: UIAlertControllerStyle.Alert);
let OkAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default) {
action in
self.dismissViewControllerAnimated(true, completion: nil);
}
myAlert.addAction(OkAction);
self.presentViewController(myAlert, animated: true, completion: nil)
}
//Validate Email Function
func validateEmail(candidate: String) -> Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"
return NSPredicate(format: "SELF MATCHES %#", emailRegex).evaluateWithObject(candidate)
}
I want to make an error that gives the user an alert when email does not exist in the firebase.
I did not know how to do that.
What I did, I gave the user an alert when error != nil
Is that the right way to do it?
The firebase website has a full listing of error codes
User Authentication
The pre-defined error codes provide info on pretty much all of the errors you may receive when authenticating or handling users.
ref.resetPassword({
email: EmailTextField.text!
}, function(error) {
if (error != nil) {
if let errorCode = FAuthenticationError(rawValue: error.code) {
switch (errorCode) {
case .UserDoesNotExist:
println("Handle invalid user")
case .InvalidEmail:
println("Handle invalid email")
case .InvalidPassword:
println("Handle invalid password")
default:
println("Handle default situation")
}
}
} else {
// Success!
}
}
According to FireBase doc here it may not always be a case. Don't put it as ErrorType, change it to error. It will be NSError type. Check code of the error you get like so:
if error != nil {
if error.description == "INVALID_USER" {
// show alert that user doesn't exists
} else {
// something went wrong
}
}
}

Resources