15
votes

I've implement Firebase Authorization to login on my iOS app via Facebook and Google. I'm coding Swift. When the app starts up I need to check whether a user is already signed-in in order to present the proper ViewController (e.g., if nobody is signed in I present the Login View Controller, otherwise I present the Home View Controller). If I use the "easy" solution offered by Firebase, meaning

if FIRAuth.auth()?.currentUser != nil {
  // User is signed in.
  // ...
} else {
  // No user is signed in.
  // ...
}

So checking if the current user is not nil, it happens exactly what the Firebase guide (https://firebase.google.com/docs/auth/ios/manage-users) alerts might happen meaning

"Note: currentUser might also be nil because the auth object has not finished initializing. If you use a listener to keep track of the user's sign-in status, you don't need to handle this case."

So I would like to implement the listener as suggested in the guide:

handle = FIRAuth.auth()?.addStateDidChangeListener() { (auth, user) in
  // ...
}

The listener will handle also intermediate status so that it is triggered when the Auth object is created. Point is I really cannot make it to work properly. Anybody can help me to use this listener in order to check if a user is logged in?

Thanks

2
where did you put that handle? and what did you do in the handle? - Xuan-Gieng Nguyen
Can you please explain what's not working? You probably don't need the handle and checking to see if user is not nil inside the closure is all that's needed. - Jay
Hi Jay. What is not working is really the fact that the currentUser is nil. And it is clear that it is just a "transition" effect because something is initializing. This is the scenario: No user logged in. I then login with Facebook. What I print in console and the presented view controller confirm me two things: the Facebook process went ok but Firebase current user is nil. And, if I go back to the app after 20 seconds, everything is ok now, the current user is not nil anymore. - Freddie Mash
@Xuan-GiengNguyen I put it in the finishLaunching function of the appDelegate so that it checks at the start where to go. - Freddie Mash
My recommendation is that you should check FIRAuth.auth()?.currentUser != nil in the finish Launching function in your appDelegate. addStateDidChangeListener can be used in other view controller to find out the state of user to adjust your UI. - Xuan-Gieng Nguyen

2 Answers

12
votes

I've implemented it like this:

FIRAuth.auth()?.addStateDidChangeListener { auth, user in
  if let user = user {
    // User is signed in. Show home screen
  } else {
    // No User is signed in. Show user the login screen
  }
}

If you don't need the User object after checking, you can replace if let user = user with a boolean test, like this:

FIRAuth.auth()?.addStateDidChangeListener { auth, user in
  if user != nil {
    // User is signed in. Show home screen
  } else {
    // No User is signed in. Show user the login screen
  }
}

Where to put the listener (from the comments):

For the cases I used to check if a user is signed in, it was enough to put it at the beginning of viewDidLoad in the specific view controller. But if you have any cases where you need to check every time you enter the specific view controller then it would be better to put it at the beginning of viewDidAppear. But I think in most cases you need to check only once, if the user enters the view

4
votes

If you're setting up the StateDidChangeListener in application:didFinishLaunchingWithOptions, you'll notice that it fires once when the listener is attached (to set the initial state, which is nil when initialising) and then again once it's finished initialising (potentially not nil). This is intended behaviour, but really impractical if you're setting it up early.

An alternative to using the listener is using NotificationCenter. This will fire once initialisation has finished:

NotificationCenter.default.addObserver(forName: NSNotification.Name.AuthStateDidChange, object: Auth.auth(), queue: nil) { _ in
    let user = Auth.auth().currentUser
}