Home > Net >  Verified users access home and unverified users stay in the login
Verified users access home and unverified users stay in the login

Time:05-21

I am checking if the user got a verification email after registration inside login function:

static func authenticate(withEmail email :String,
                         password:String,
                         completionHandler:@escaping (Result<Bool, EmailAuthError>) -> ()) {
    Auth.auth().signIn(withEmail: email, password: password) { (authResult, error) in
        // check the NSError code and convert the error to an AuthError type
        var newError:NSError
        if let err = error {
            newError = err as NSError
            var authError:EmailAuthError?
            switch newError.code {
            case 17009:
                authError = .incorrectPassword
            case 17008:
                authError = .invalidEmail
            case 17011:
                authError = .accoundDoesNotExist
            default:
                authError = .unknownError
            
            }
            completionHandler(.failure(authError!))
        } else {
            if (Auth.auth().currentUser!.isEmailVerified == true){
            completionHandler(.success(true))
                print ("Email Verified")
            }
            else {
                print ("Email not verified")
            }
        }
    }
}

This function will be called after clicking "Login" button where I reload user to make sure that (isEmailVerified) value updated.

Button(action: {
            //Sign In Action
            FBAuth.reloadUser()
            FBAuth.authenticate(withEmail: self.user.email, password: self.user.password) { (result) in
                switch result {
                case .failure(let error):
                    self.authError = error
                    self.showAlert = true
                case .success( _):
                    print ("Signed in.")
                }
            }
         
        }

The Content View where I am checking the user's login status, I did not update it after I added isEmailVerified to my code coz I have only three cases undefined, signedIn, signedOut, I don't know how to get out of this box:

struct ContentView: View {
@EnvironmentObject var userInfo : UserInfo
@State var shouldShowSignUp : Bool = false
var body: some View {
    Group {
      if shouldShowSignUp {
       SignupView()
        }
      else  {
        ZStack{
if userInfo.isUserAuthenticated == .undefined{ SignupView()}
else if userInfo.isUserAuthenticated == .signedIn && Auth.auth().currentUser!.isEmailVerified == true {HomeView()} 
else if userInfo.isUserAuthenticated == .signedIn && Auth.auth().currentUser!.isEmailVerified == false {LoginView()}
          
      SplashScreen()
            }
        }
    }
    
    .onAppear {
        self.userInfo.configureFirebaseStateDidChange()
    }
}}

UserInfo :

import Foundation
import FirebaseAuth

class UserInfo : ObservableObject{
enum FBAuthState{
    case undefined, signedOut, signedIn
}
@Published var isUserAuthenticated : FBAuthState = .undefined
@Published var user : FBUser = .init(uid: "", name: "", email: "")

var authStateDidChangeListenerHandle : AuthStateDidChangeListenerHandle?

func configureFirebaseStateDidChange (){
    authStateDidChangeListenerHandle = Auth.auth().addStateDidChangeListener({ (_,user) in
        guard let _ = user else {
            self.isUserAuthenticated = .signedOut
            return
        }
        self.isUserAuthenticated = .signedIn

    })
    
}
 
}

Everything is going fine. I get the correct value for isEmailVerified. My question is how to give verified users access to the homeView and unverified users they will get alert that their email needs to be verified and they will stay in the loginView.

CodePudding user response:

As you didn´t post your Views I have to answer with an abstract answer.

in your main App struct

@main
struct TestApp1App: App {
    @Environment(\.scenePhase) private var scenePhase
    @AppStorage("emailVerified") private var emailVerified: Bool = false
    var body: some Scene {
        WindowGroup {
            if emailVerified{
                LoginView()
            }else{
                HomeView()
            }                
        }
    }
}

And in your LoginView:

// Add this
@AppStorage("emailVerified") private var emailVerified: Bool = false

 Button(action: {
        //Sign In Action
        FBAuth.reloadUser()
        FBAuth.authenticate(withEmail: self.user.email, password: self.user.password) { (result) in
            switch result {
            case .failure(let error):
                self.authError = error
                self.showAlert = true
            case .success( _):
                print ("Signed in.")
                emailVerified = true //add this
            }
        }
     
  }

Edit:

But this doesn´t change the prime layout of my answer. For example you don´t persist the verificationState. So you have to do it all over again.

In your case create the UserInfo class in your app struct and check the isUserAuthenticated property in the if statement.

Edit 2:

This can be simplyfied in a lot of ways. My suggestion:

ContentView should be something like this:

struct ContentView: View {
    @EnvironmentObject var userInfo : UserInfo
    // pull the vars from persisted store this will also update your view when changed
    @AppStorage("authState") var authState: FBAuthState = .undefined
    @AppStorage("emailVerificationState") var emailVerificationState: Bool = false
    var body: some View {
        
        Group{
            if authState == .undefined || !emailVerificationState{
                SignupView()
            } else {
                ZStack{
                    if authState == .signedIn{
                        HomeView()
                    } else{
                        LoginView()
                    }
                    SplashScreen()
                }
            }
        }
        .onAppear {
            self.userInfo.configureFirebaseStateDidChange()
        }
    }
}

Change your UserInfo to:

enum FBAuthState: Int{
    case undefined, signedOut, signedIn
}

class UserInfo : ObservableObject{
    
    @AppStorage("authState") var authState: FBAuthState = .undefined
    @Published var user : FBUser = .init(uid: "", name: "", email: "")
    
    var authStateDidChangeListenerHandle : AuthStateDidChangeListenerHandle?
    
    func configureFirebaseStateDidChange (){
        authStateDidChangeListenerHandle = Auth.auth().addStateDidChangeListener({ (_,user) in
            guard let _ = user else {
                self.authState = .signedOut
                return
            }
            self.authState = .signedIn
            
        })
        
    }
}

I don´t know where this button lives but in that class or view add:

@AppStorage("emailVerificationState") var emailVerificationState: Bool = false

and do:

Button(action: {
    //Sign In Action
    FBAuth.reloadUser()
    FBAuth.authenticate(withEmail: self.user.email, password: self.user.password) { (result) in
        switch result {
        case .failure(let error):
            emailVerificationState = false
            self.authError = error
            self.showAlert = true
        case .success( _):
            emailVerificationState = true
            print ("Signed in.")
        }
    }
    
}

Edit 3:

  • You would need to import SwiftUI module where you want to use @AppStorage property wrapper.
  • Please see my answer. I moved FBAuthState out of your class to make it visible outside. And you would need to derive from Int.

Edit 4:

Group{
        if authState == .undefined {
            SignupView()
        } else {
            ZStack{
                if authState == .signedIn && emailVerificationState{
                    HomeView()
                } else{
                    LoginView()
                }
                SplashScreen()
            }
        }
    }
  • Related