Login authentication problem with firebase in Xcode - ios

I try to do firebase authenticate. I've installed all necessary pods and made all needed methods. When I click "Login" even when I entered bad email and password, I get error message (e.g. bad password) and immediately my view is changed to my target view (next ViewController). I don't know why it is happening, because I made if statement. I want to have my login page unless I enter correct email and password.
#IBAction func loginTapped(_ sender: UIButton) {
if let email = emailTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), let password =
passwordTextField.text?.trimmingCharacters(in:
.whitespacesAndNewlines) {
Auth.auth().signIn(withEmail: email, password: password) { (authResult, error) in
if let e = error {
// Can't sign in
self.errorLabel.text = error!.localizedDescription
self.errorLabel.alpha = 1
} else {
self.performSegue(withIdentifier: "loginToHome", sender: self)
}
}
}
}

You may try to check if an error exists first. If yes, the error message will be shown. If there's no error, the login success segue will be performed.
#IBAction func loginTapped(_ sender: UIButton) {
if let email = emailTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), let password = passwordTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) {
Auth.auth().signIn(withEmail: email, password: password) { (authResult, error) in
if error != nil { // there's an error - show error label
self.errorLabel.text = error!.localizedDescription
self.errorLabel.alpha = 1
} else { //no error - perform segue
// no error - perform segue
self.performSegue(withIdentifier: "loginToHome", sender: self) } } } }
Edit: You may also add return after checking that an error exists - the codes that follow after it will not be performed:
#IBAction func loginTapped(_ sender: UIButton) {
if let email = emailTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), let password = passwordTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) {
Auth.auth().signIn(withEmail: email, password: password) { (authResult, error) in
if error != nil { // there's an error - show error label
self.errorLabel.text = error!.localizedDescription
self.errorLabel.alpha = 1
return // activities that follow will not be performed
} else { //no error - perform segue
self.performSegue(withIdentifier: "loginToHome", sender: self) } } } }

Related

Firebase Email Verification Swift

I tried to use the Firebase Authentication, but in the login lets me in even when I haven't used the email to confirm the verification.
I have two Viewcontroller , one for Login and the other for signUP.
I can log in and I get the email for verification, but I can also log in in without verification.
public func sendVerificationMail() {
if self.authUser != nil && !self.authUser!.isEmailVerified {
self.authUser!.sendEmailVerification(completion: { (error) in
// Notify the user that the mail has sent or couldn't because of an error.
})
} else {
// Either the user is not available, or the user is already verified.
}
}
#IBAction func signupButtonTapped(_ sender: Any) {
print("Sign up button tapped")
Auth.auth().createUser(withEmail: self.userEmailTextField.text!, password: self.userPasswordTextField.text!) { (user, error) in
if user != nil {
print("User has Signed Up")
self.sendVerificationMail()
}
if error != nil {
print("User cant Sign Up")
}
}
}
#IBAction func signinButtonTapped(_ sender: Any) {
Auth.auth().signIn(withEmail: self.userEmailTextField.text!, password: self.userPasswordTextField.text!) { (user, error) in
if user != nil {
print("User has Signed In")
}
if error != nil {
print("Cant Sign in user")
} else {
self.performSegue(withIdentifier: "toHome", sender: nil)
}
}
}
Firebase Auth doesn't prevent people from signing in when their email isn't verified. If you want to prevent users from advancing when they aren't verified, you will need to code this on the client using the isEmailVerified boolean.
Auth.auth().signIn(withEmail: self.userEmailTextField.text!, password: self.userPasswordTextField.text!) { (authResult, error) in
if let authResult = authResult {
let user = authResult.user
print("User has Signed In")
if user.isEmailVerified {
self.performSegue(withIdentifier: "toHome", sender: nil)
} else {
// do whatever you want to do when user isn't verified
}
}
if let error = error {
print("Cant Sign in user")
}
}
A more comprehensive solution than that provided by #jen-person (and based on together with another SO answer) is this:
final class MySignInView: UIView { // or possibly MySignInViewController: UIViewController
// ... your properties etc...
#IBAction func signInButtonPressed(sender _: AnyObject) {
trySigningIn()
}
}
// MARK: Private
private extension MySignInView {
func trySigningIn() {
guard
let email = userEmailTextField.text,
let password = userPasswordTextField.text
else {
print("Cannot sign in, email or password is 'nil'")
return
}
do {
try signIn(email: email, password: password) { [unowned self] authResult in
self.userDidSignIn(authResult.user)
}
} catch {
// Display error
}
}
func userDidSignIn(_ user: FIRUser) {
// Creds to Jen: https://stackoverflow.com/a/51389154/1311272
guard user.isEmailVerified else {
// TODO display message about non verified email user?
return
}
performSegue(withIdentifier: "toHome", sender: nil)
}
}
// MARK: Error
private extension MySignInView {
enum Error: Strng, Equatable {
case invalidEmail
case userDisabled
case wrongPassword
case userNotFound
case networkError
case unknownError
}
}
// MARK: Firebae specific
private extension MySignInView {
func signIn(
email: String,
password: String,
onSuccessful: (AuthDataResult) -> Void
) throws {
Auth.auth().signIn(
withEmail: email,
password: password
) { (authResult, anyError) in
if let anyError = anyError {
if let error = Error(anyError: anyError) {
throw error
} else {
fatalError("Unsupported error: \(anyError)")
}
}
onSuccessful(authResult)
}
}
}
private extension MySignInView.Error {
init?(anyError: Swift.Error) {
guard let authErrorCode = FIRAuthErrorCode(rawValue: anyError.code) else {
return nil
}
self.init(fireBaseAuthErrorCode: authErrorCode)
}
// Creds goes to to: https://stackoverflow.com/a/39936083/1311272
init(fireBaseAuthErrorCode: FIRAuthErrorCode) {
switch errCode {
case .ErrorCodeInvalidEmail:
self = .invalidEmail
case .ErrorCodeUserDisabled:
self = .userDisabled
case .ErrorCodeWrongPassword:
self = .wrongPassword
case .ErrorCodeUserNotFound:
self = .userNotFound
case .ErrorCodeNetworkError:
self = .networkError
default:
self = .unknownError
}
}
}
You need to inform the user appropriately about the different errors/incorrect states of course, not only print.
This code should probably me moved to a ViewModel, and if your are not using MVVM, I highly recommend it :).
if let email = emailTextfield.text, let password = passwordTextfield.text {
Auth.auth().signIn(withEmail: email, password: password) { authResult, error in
if let e = error{
print(e.localizedDescription)
}
else {
//Do whatever you want to do after successful login
}
}
}
You can try this also:
if( !Auth.auth().currentUser!.isEmailVerified) {
// do something
}

Firebase Error Value of type 'AuthDataResult' has no member 'displayName'

I'm following along Rob Percival's iOS developer course and I am currently stuck at the "Uber clone" project. Following is an extract of the problematic code. The line of code in which the error occurs is:
if user?.displayName == "Lehrer" {
which is found after the comment //LOG IN
#IBAction func topTapped(_ sender: Any) {
if emailTextField.text == "" || passwordTextField.text == "" {
displayAlert(title: "Achtung", message: "Du musst sowohl eine Email-Adresse als auch Passwort eingeben!")
} else {
if let email = emailTextField.text {
if let password = passwordTextField.text {
if signUpMode {
// SIGN UP
Auth.auth().createUser(withEmail: email, password: password, completion: { (user, error) in
if error != nil {
self.displayAlert(title: "Error", message: error!.localizedDescription)
} else {
if self.benutzerLehrerSwitch.isOn {
//Lehrer
let req = Auth.auth().currentUser?.createProfileChangeRequest()
req?.displayName = "Lehrer"
req?.commitChanges(completion: nil)
self.performSegue(withIdentifier: "lehrerSegue", sender: nil)
} else {
// RIDER
let req = Auth.auth().currentUser?.createProfileChangeRequest()
req?.displayName = "Benutzer"
req?.commitChanges(completion: nil)
self.performSegue(withIdentifier: "benutzerSegue", sender: nil)
}
}
})
} else {
// LOG IN
Auth.auth().signIn(withEmail: email, password: password, completion: { (user, error) in
if error != nil {
self.displayAlert(title: "Error", message: error!.localizedDescription)
} else {
if user?.displayName == "Lehrer" {
// BENUTZER
self.performSegue(withIdentifier: "benutzerSegue", sender: nil)
} else {
// LEHRER
self.performSegue(withIdentifier: "lehrerSegue", sender: nil)
}
}
})
}
}
}
}
}
Basically, I allow users to sign up as one of two different types of roles ("Lehrer" or "Benutzer") and depending on which of those they choose, they should see different screens after logging in. I use Firebase for the backend of this code and signing up User's accounts works fine if I delete the faulty code. Does anyone have any ideas?
You can see the exact look in this screenshot:
Google has updated the methods after Firebase 5+ update
try this
self.name = user.user.displayName
put user?.user.displayName instead of user?.displayName

Terminating app due to uncaught exception Swift

I currently have login authentication working, as well as registration with firebase. I now wanted to keep my user logged in, which is working. The problem I am facing is when I log out, I try to log back in and I receive this error:
// Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver () has no segue with identifier 'chatRoom''\
Which does not make sense, because when I first log in, it brings me to the ChatRoom Viewcontroller using the chatRoom segue.
This is my code below:
func handleLogin() {
guard let email = emailTextField.text, let password = passwordTextField.text
else {
print("Form is not valid")
return
}
Auth.auth().signIn(withEmail: email, password: password, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
self.loginErrorAlert("Error!", message: "Username or password incorrect, please try again.")
return
}
// successfully logged in our user and keep user logged in until they logout
if Auth.auth().currentUser != nil {
UserDefaults.standard.set(Auth.auth().currentUser!.uid, forKey: "loggedIn")
UserDefaults.standard.synchronize()
self.performSegue(withIdentifier: "chatRoom", sender: self)// this is the error I am facing
}
})
}
func handleRegister() {
guard let email = emailTextField.text, let password = passwordTextField.text, let name = nameTextField.text else {
print("Form is not valid")
signupErrorAlert("Error!", message: "Could not be Registered at this time, please try again.")
return
}
Auth.auth().createUser(withEmail: email, password: password, completion: { (user: User?, error) in
if error != nil {
print(error!.localizedDescription)
self.loginErrorAlert("Error!", message: "Could not be Registered at this time, please try again later.")
return
}
guard let uid = user?.uid else {
return
}
// successfully authenticated user and keep logged in until they logout
let ref = Database.database().reference(fromURL: "https://boccighub.firebaseio.com/")
let usersReference = ref.child("users").child(uid)
let values = ["name": name, "email": email]
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
if err != nil {
print(err!.localizedDescription)
return
}
if Auth.auth().currentUser != nil {
UserDefaults.standard.set(Auth.auth().currentUser!.uid, forKey: "loggedIn")
UserDefaults.standard.synchronize()
self.performSegue(withIdentifier: "chatRoom", sender: self)
}
})
})
}
// User logged out
#IBAction func handleLogout(_ sender: Any) {
do {
try Auth.auth().signOut()
print("user signedout")
if Auth.auth().currentUser == nil {
print("No user, key removed")
UserDefaults.standard.removeObject(forKey: "loggedIn")
UserDefaults.standard.synchronize()
print("User logged out")
let loginController = LoginViewController()
present(loginController, animated: true, completion: nil)
}
} catch let logoutError {
print(logoutError)
}
}
Fixed. I Removed the let loginController = LoginViewController(),
I created a new segue and now do self.performSegue(withIdentifier: "logOut", sender: self), works perfectly

Users won't show up on Authentication tab in Firebase

Why is it that when I type in an email and password nothing shows up in Firebase? I've done it before with no issues.
No user entry is made when i check my Authentication tab in firebase.
//Sign up user
let email = emailTextField.text
let password = passwordTextField.text
FIRAuth.auth()?.createUser(withEmail:email! , password: password!, completion: { (user, error) in
if error != nil {
self.login()
} else {
print("User created!")
self.login()
}
})
}
#IBAction func signInButton(_ sender: Any) {
}
//Functions
func login(){
FIRAuth.auth()?.signIn(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: { (user, error) in
if error != nil {
print("Password or email has not been entered correctly")
} else {
print("Successful Login")
}
})
}
}
I don't know if it will fix your problem, but try declaring with a guard statement, and remove your bangs (!) in the signup/login. It should at least be a lot less error prone.
Guard let email = emailTextField.text else { return }
Guard let password = passwordTextField.text else { return }
Now remove bangs so it looks like this
FIRAuth.auth()?.createUser(withEmail: email , password: password, completion: { (user, error) in
And do the same for your login function, unwrap your textField texts with a guard statement.
Cheers.
I was using the wrong GoogleService-info.plist file. I kept using the same one because i didn't think i had to keep re-downloading them for each app.

Firebase automatic login on iOS

Is there a better way to make a user automatically login to the app as long as they have logged in in the past; other than saving their login details directly to the storage which may be insecure?
Thanks
PS: I'm using Firebase and Swift for iOS
EDIT: Here's the code
import UIKit
import Firebase
class loginVC: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var errorLabel: UILabel!
override func viewDidAppear(animated: Bool) {
automaticLogin()
}
#IBAction func loginTapped(sender: AnyObject) {
errorLabel.text = ""
FIRAuth.auth()?.createUserWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: {
user, error in
if error != nil {
FIRAuth.auth()?.signInWithEmail(self.emailTextField.text!, password: self.passwordTextField.text!, completion: { (user, error) in
if error == nil {
self.login()
} else if error != nil {
self.errorLabel.text = ("Invalid email address or password")
print(error)
}
})
} else {
print("user created")
self.login()
print(user?.displayName)
}
})
}
#IBAction func forgotPasswordTapped(sender: AnyObject) {
let email = emailTextField.text
errorLabel.text = ""
FIRAuth.auth()?.sendPasswordResetWithEmail(email!) { error in
if error != nil {
self.errorLabel.text = ("Invalid email address")
print(error)
} else {
self.errorLabel.text = ("Password reset email successfully sent")
print("password reset email sent")
}
}
}
func login() {
FIRAuth.auth()?.signInWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: {
user, error in
if error != nil {
self.errorLabel.text = ("Invalid email address or password")
print(error)
} else {
print("login successful")
self.checkIfUserIsNew()
}
})
}
func checkIfUserIsNew() {
if FIRAuth.auth()?.currentUser?.displayName != nil || FIRAuth.auth()?.currentUser?.displayName == "" {
self.performSegueWithIdentifier("showChatVC", sender: self)
} else {
self.performSegueWithIdentifier("showOptionsVC", sender: self)
}
}
func automaticLogin() {
FIRAuth.auth()?.addAuthStateDidChangeListener { auth, user in
if user == user {
print("still signed in")
} else {
print("not signed in")
}
}
}
I also used this to logout the user:
#IBAction func logOutTapped(sender: AnyObject) {
try! FIRAuth.auth()?.signOut()
performSegueWithIdentifier("showLoginVC", sender: self)
print(FIRAuth.auth()?.currentUser?.displayName)
}
You might want to use NSUserDefaults. To do this in your login() function you might want to save your user's data once they signed in or create an account. Inside your login function you can start by creating new variable to grab your user's email / password.
let userDefaults = UserDefaults.standard
userDefaults.setValue(self.emailField.text!, forKey: "email")
userDefaults.setValue(self.passField.text!, forKey: "password")
Next, you also need to implement a new function called viewWillAppear, which will be called before the view controller. And you might also put some arguments inside your login function.
override func viewWillAppear(_ animated: Bool) {
let userDefaults = UserDefaults.standard
if userDefaults.string(forKey: "email") != nil {
let email = userDefaults.string(forKey: "email")
let password = userDefaults.string(forKey: "password")
login(email: email!, password: password!)
}
}
the if statement will check wether your user already registered their email account or not yet. Lastly you need to update your login function to have two parameters, to pass in the email and password of the user.

Resources