Firebase - iOS Swift: FIRAuth.auth().signOut() not signing out current user

10,219

Solution 1

You can add a listener in your viewDidAppear method of your view controller like so:

FIRAuth.auth()?.addStateDidChangeListener { auth, user in
    if let user = user {
        print("User is signed in.")
    } else {
        print("User is signed out.")
    }
}

This allows you to execute code when the user's authentication state has changed. It allows you to listen for the event since the signOut method from Firebase does not have a completion handler.

Solution 2

GIDSignIn.sharedInstance().signOut()

Solution 3

I actually had this issue as well. I was also logging out the user (as you are) with the method's provided by Firebase but when I printed to the console it said that I still had a optional user.

I had to change the logic of setting the current user so that it is always configured by the authentication handler provided by Firebase:

var currentUser: User? = Auth.auth().currentUser
var handle: AuthStateDidChangeListenerHandle!

init() {

    handle = Auth.auth().addStateDidChangeListener { (auth, user) in
        self.currentUser = user

        if user == nil {
             UserDefaults.standard.setValue(false, forKey: UserDefaults.loggedIn)
        } else {  
             UserDefaults.standard.setValue(true, forKey: UserDefaults.loggedIn)
        }

    }

}

As long as you are referencing the current user from this handle, it will update the current user no matter the authentication state.

Solution 4

Some answers are using a force unwrap when the firebase signing out method can throw an error. DO NOT DO THIS!

Instead the call should be done in a do - catch - block as shown below

   do {
        try Auth.auth().signOut()
    } catch let error {
        // handle error here
        print("Error trying to sign out of Firebase: \(error.localizedDescription)")
    }

You can then listen to the state change using Auth.auth().addStateDidChangeListener and handle accordingly.

Solution 5

Use exclamation points not question marks.

  try! FIRAuth.auth()!.signOut()
Share:
10,219
alexisSchreier
Author by

alexisSchreier

Updated on June 15, 2022

Comments

  • alexisSchreier
    alexisSchreier almost 2 years

    I'm building an app using Firebase with an initial SignInViewController that loads a sign in page for users to authenticate with email which triggers the following methods:

    @IBAction func didTapSignIn(sender: AnyObject) {
        let email = emailField.text
        let password = passwordField.text
        FIRAuth.auth()?.signInWithEmail(email!, password: password!) { (user, error) in
            if let error = error {
                print(error.localizedDescription)
                return
            }
            self.signedIn(user!)
        }
    }
    
    func signedIn(user: FIRUser?) {
        AppState.sharedInstance.displayName = user?.displayName ?? user?.email
        AppState.sharedInstance.signedIn = true
        NSNotificationCenter.defaultCenter().postNotificationName(Constants.NotificationKeys.SignedIn, object: nil, userInfo: nil)
        performSegueWithIdentifier(Constants.Segues.SignInToHome, sender: nil)
    }
    

    The SignInViewController also checks if there is a cached current user when the app launches and, if so, signs that user in:

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(true)
        //Synchronously gets the cached current user, or null if there is none.
        if let user = FirebaseConfigManager.sharedInstance.currentUser {
            self.signedIn(user)
        }
    }
    

    Once the user is signed in, the app segues to a HomeScreenViewController which displays a "Sign Out" button at the top left of the navigation bar. When a user taps the "Sign Out" button, that user is supposed to get signed out and the app should segue back to the SignInViewController with the following method:

    @IBAction func didTapSignOut(sender: UIBarButtonItem) {
        print("sign out button tapped")
        let firebaseAuth = FIRAuth.auth()
        do {
            try firebaseAuth?.signOut()
            AppState.sharedInstance.signedIn = false
            dismissViewControllerAnimated(true, completion: nil)
        } catch let signOutError as NSError {
            print ("Error signing out: \(signOutError)")
        } catch {
            print("Unknown error.")
        }
    }
    

    When I tap the "Sign out" button, the didTapSignOut method gets called and gets executed. However, after the try firebaseAuth?.signOut() line of code gets executed, the current user should be nil. But when I print out the current user in the Xcode console, the current user is still logged in:

    po FIRAuth.auth()?.currentUser
    ▿ Optional<FIRUser>
      - Some : <FIRUser: 0x7fde43540f50>
    

    Since the current user doesn't get signed out after firebaseAuth?.signOut() gets called, once the app segues back to the SignInViewController the app still thinks there is a cached current user so that user gets signed in again.

    Could this be a Keychain issue?

    Does it have to do with NSNotificationCenter.defaultCenter().postNotificationName being called?

    My code comes directly from the Google Firebase Swift Codelab so I'm not sure why it's not working: https://codelabs.developers.google.com/codelabs/firebase-ios-swift/#4

  • alexisSchreier
    alexisSchreier over 7 years
    Thanks for your answer @rMickeyD. I'm still getting the same result though. I edited my code to try! FIRAuth.auth()!.signOut() like you suggested but the current user still exists in cache when viewWillAppear(animated:Bool) gets called and checks for the user in the SignInViewController.
  • AnBisw
    AnBisw over 6 years
    never Force unwrap (!) a try, your app will crash if you force unwrap and the signOut does encounter an error. For example, signOut will most definitely throw an error if the user has no internet connectivity (or in Airplane mode), in this case your app will most definitely crash.