FirebaseUI Authentication for iOS

iOS Swift

By far, I have found that FirebaseUI is the easiest way to implement User Authentication to an iOS App. By the end of this tutorial, you’re going to see that in action with just adding a few lines of code.

As the name suggests, FirebaseUI is shipped with a prebuilt UI that gives view components for login flow out of the box.

FirebaseUI supports almost all the major OAuth providers listed below (at least at the time of writing this article).

  • Email/Password (you are here)
  • Phone
  • Google
  • Facebook
  • Twitter
  • Apple
  • Github
  • Microsoft
  • Anonymous

For simplicity’s sake, I am going to stick with Email/Password Authentication and show you how to implement it.

Add Firebase to an iOS App

Adding Firebase SDK to the Xcode project will be the first step before using any of its products.

To keep this article short, I am going to point to my other article that shows how to add Firebase to the Xcode project from start to finish with STEP by STEP instructions.

Once Firebase is added to your Xcode project, you’re ready to use any of its products.

Enable Email/Password Sign-in Method

To use Email/Password Sign-in, I am going to enable it on the Firebase Console.

So, go to Firebase DashboardAuthenticationSign-in method tab and open up the Email/Password option by clicking on it.

Then, toggle the enable button and save it.

Install FirebaseUI Pods

Here are the pods that need to be installed into the project in order to use FirebaseUI Email/Password Authentication.

pod ‘FirebaseUI/Auth’
pod ‘FirebaseUI/Email’

Open up the Podfile from the Project Navigator and add them right below the pod ‘Firebase/Core’ line.

Then, install them by opening the project directory on the Terminal window and run.

pod install

This will take a few seconds to complete the installation process.

Once it’s done, we’re ready to add Email/Password Auth using FirebaseUI.

Check User Authentication Status

The first step is to import FirebaseUI at the top in the ViewController.swift file.

import FirebaseUI

The plan is…

If a user is logged in, show the user information such as:

  • Email
  • Display Name

If not, show the login view.

Simple enough…

The best place to check this is inside the viewWillAppear() method. As the name suggests, it will be called when our ViewController() is about to appear on the screen.

override func viewWillAppear(_ animated: Bool) {
}

Firebase provides a method called addStateDidChangeListener() to check the user’s authentication status.

So we can simply call this method inside the viewWillAppear() to do the check.

override func viewWillAppear(_ animated: Bool) {
    Auth.auth().addStateDidChangeListener { (auth, user) in
    }
}

Inside this method, the user parameter in the closure is optional which will hold one of these two values:

  • User Object
  • Nil
override func viewWillAppear(_ animated: Bool) {
    Auth.auth().addStateDidChangeListener { (auth, user) in
        if let user = user {
            self.showUserInfo(user:user)
        } else {
            self.showLoginVC()
        }
    }
}

If a user is logged in, I can safely unwrap it using the if let syntax and call the showUserInfo(user:user) function which I am going to define in just a moment.

Similar to that, call showLoginVC() if no user is logged in.

Let’s go ahead and declare both functions.

func showUserInfo(user:User) { }
func showLoginVC() { }

Instantiate AuthViewController()

Let’s add the authViewController, which is part of FirebaseUI.

func showLoginVC() {
    let authUI = FUIAuth.defaultAuthUI()
    let providers = [FUIEmailAuth()]
    authUI?.providers = providers
    let authViewController = authUI!.authViewController()
    self.present(authViewController, animated: true, completion: nil)
}

The first step is to create an instance of an authUI object by running the defaultAuthUI() method on the FUIAuth object.

The providers variable is an array of FUI sign-in method objects. For the Email/Password method, instantiate FUIEmaiAuth() inside the array.

This is where you’re going to be adding more sign-in providers, such as Facebook and Google, etc and the rest will be magic.

Then, assign providers to the provider’s property of the authUI object.

After that, create a login view controller by invoking authViewController on the authUI object.

Finally, show it on the view hierarchy using the present() method.

When you run the app, it will show the prebuilt Auth View Controller with the “Sign in with email” button.

Add A Brand New Firebase User

There is not just that one view controller() that you see, but it actually has 3 separate view controllers.

  • Login View Controller
  • Email View Controller
  • Password View Controller

When the “Sign in with email” button is pressed, it will go to the next view controller that has an email field in it.

When a user enters an email and hits next, at that stage it checks to see if the provided email already has an account or not.

If not, it takes you to the next view controller that has three fields in it.

  • Email Field( filled with the email that is provided from the previous screen) *
  • Name Field *
  • Password *

If a user already has a Firebase account, it will just show the email field already completed and all you have to do is enter the password to log in.

It will also check the validation. For example, the password must be 6 characters or more.

Hit save, and if everything goes well, you’ll be able to see a brand new Firebase user on the Authentication Page.

As you can see, I got all the functionalities with a few lines of code, which is why FirebaseUI is my go-to place when it comes to User Authentication.

Show User Info & Logout Button

Let’s add a name label and the log out button when a user is in the signed-in state.

I am going to be building the two views programmatically instead of using a storyboard.

Here are my views on why I chose the programmatic approach over storyboard.

private let nameLabel: UILabel = {
    let label = UILabel()
    label.backgroundColor = .systemFill
    label.textAlignment = .center
    return label
}()

private let logOutButton: UIButton = {
    let button = UIButton()
    button.backgroundColor = .systemRed
    button.setTitle("Log out", for: .normal)
    return button
}()

Add them to the view hierarchy inside the showUserInfo() function.

func showUserInfo(user:User) {
    
    nameLabel.frame = CGRect(x: 20, y: view.bounds.height / 3, width: view.bounds.width - 40, height: 50)
    logOutButton.frame = CGRect(x: 20, y: view.bounds.height / 3 + 50,  width: view.bounds.width - 40, height: 50)
    view.addSubview(nameLabel)
    view.addSubview(logOutButton)
}

Finally, show the username by setting user.displayName to the nameLabel.

nameLabel.text = user.displayName

Logout User

The last step is to attach a touch event to the logout button.

logOutButton.addTarget(self, action: #selector(logoutButtonPressed), for:.touchUpInside)

Inside the logout callback function, call signOut() method on the Auth object.

@objc func logoutButtonPressed() {
  do {
    try Auth.auth().signOut()
  } catch let err {
    print(err)
  }
}

When the signout button is pressed, the Auth View Controller() is presented to the screen.

That’s because when a login state changes, the addStateDidChangeListener() method will be called. If no user is signed in, it will bring the Auth View Controller() on the screen.

Nice!

However, there are a couple of usability issues here.

Problem #1

Users will be able to swipe down and not be able to see the sign-in screen again.

Solution

Make the Auth View Controller cover the whole screen by adding the following line inside the showLoginVC() function.

authViewController.modalPresentationStyle = .overCurrentContext

Problem #2

Having the cancel button on the top left will do the same.

Solution

Get rid of it by adding the following code after the ViewController class definition.

class ViewController: UIViewController {
    ...
}

extension FUIAuthBaseViewController{
    open override func viewWillAppear(_ animated: Bool) {
        self.navigationItem.leftBarButtonItem = nil
    }
}