To begin setting up our project with Firebase we need to setup our project within the Firebase console. I’m not going to cover how to do this here as the documentation does a pretty great job of outlining this. We need to begin by following the instructions to install the Firebase SDK, followed by adding our iOS app to our Firebase project so that we can enable the authentication features for our application.
Now that we have Firebase setup within the console, we need to add the required dependencies to our application. We’re going to do this by adding two declarations to our pod file:
pod 'Firebase/Core'
pod 'Firebase/Auth'
Once these have been added we can go ahead and run pod install so that the Firebase dependencies become available for use in our application.
Now that these are ready for use we’re going to configure Firebase in our application delegate class. Here we need to start by importing the Firebase SDK:
import Firebase
Followed by the call to the configure() function from the SDK. This must be called in-order for us to be able to access Firebase services, because of this we’re going to go ahead and call this within the application function of our delegate to ensure that this is configured at the start-up point of our app:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { ... FirebaseApp.configure() ... }
Now that Firebase is configured, we’re ready to use the authentication services that it provides in it’s Auth package. However, before we start diving into triggering the actual authentication flow we’re going to take a look at implementing the auth state listener that Firebase Auth provides. For this we need to begin by declaring a field reference AuthStateDidChangeListenerHandle class:
var handle: AuthStateDidChangeListenerHandle?
This class is a listener which allows us to register for authentication state events on our Firebase Auth instance — allowing us to observe whether or not the user is currently authenticated in our app. We can go ahead and instantiate this listener by using Firebase Auth to retrieve the current auth instance of our Firebase App (using auth()), followed by a call to the addStateDidChangeListener() function:
handle = Auth.auth().addStateDidChangeListener { (auth, user) in // Handle authenticated state }
When we call this function we kick off the observation of the authentication state and retrieve back the AuthStateDidChangeListenerHandle. This allows us to keep a reference to our observation and remove the listener during the teardown of our application using the removeStateDidChangeListener() function, passing in our handle reference as an argument:
Auth.auth().removeStateDidChangeListener(handle)
Now that we have the observation configured, we need to actually do something with the data we get back from the listener. In our sample app we’re essentially going to want to show two different states:
- A login screen when the user isn’t authenticated
- A home screen when the user is authenticated
So to satisfy this we’re essentially going to set our root view controller based on whether or not the current user from our auth instance is nil or not:
private func observeAuthorisedState() { self.setupRootViewController( viewController: SplashViewController())
handle = Auth.auth().addStateDidChangeListener { (auth, user) in if user == nil { self.setupRootViewController( viewController: LoginViewController()) } else { self.setupRootViewController( viewController: HomeViewController()) } } }
private func setupRootViewController( viewController: UIViewController) { self.window!.rootViewController = viewController self.window!.makeKeyAndVisible() }
Now, whenever the authentication state of our Firebase instance changes our observer will receive the state and set our root view controller based off of this value.
At this point we are managing the authenticated state of our user, but we haven’t actually implemented the ability for our users to authenticate. Luckily for us, Firebase has made this process pretty straightforward for us. Whilst there are many methods for implementing authentication with Firebase (Facebook, Twitter, phone, email etc), for this example we’re only going to add email authentication to our app. For this, we’re going to use the signInWithEmail() function which takes an:
- Email address for the sign-in process
- Password for the sign-in process
- FIRAuthDataResultCallback to listen for the result of the authentication process
Auth.auth().signIn(withEmail: emailField.text, password: passwordField.text) { (user, error) in
}
When we trigger this authentication process, signIn() will kick of an the authentication process using the given credentials as an asynchronous task, using the callback to provide us with the value of the result. This result will contain either:
- An instance of a FIRAuthError which we can handle accordingly. This will be nil if there hasn’t been an error during the authentication process.
- An instance FIRAuthDataResult which contains both the FIRUser instance for the authenticated user, along with FIRAdditionalUserInfo for further details on the authenticated user.
Now at this point because we’re using the auth state change listener all we need to do is handle the error state of the sign-in process, by simply checking if the error returned is nil.
if (error != nil) { self.showToast(message: error!.localizedDescription) }
If we weren’t using the auth state change listener then we would need to check whether or not the user field in the callback was nil and handle the UI accordingly.
To see a full list of different ways in which you can authenticate the user, check out the full documentation here. The great thing is that they all pretty much operate and look the same, so using a different authenticator than email will work in a similar way as to how we have implemented it above.
If we want to provide registration from our app, then we can use the createUser() function to do so. This works the same way as the signIn() function we just looked at, however it will create an account rather than looking for a corresponding account in Firebase:
Auth.auth().createUser(withEmail: emailField.text, password: passwordField.text) { (user, error) in
}
Now that our user can sign-in, we also want to allow them to sign-out — this can be achieved by using the signOut() function from the Auth library. This function will return a boolean to represent its success state, as well as throwing an error if something went wrong during the process. For this examples sake, let’s catch the error if if occurs here:
do { try Auth.auth().signOut() } catch let signOutError as NSError { // Show error message }
Because we’re using the auth state change listener inn our application delegate, when the sign-out process is triggered our root view controller will automatically be replaced with the Authentication screen — this means that we do not need to handle the success state in our View Controller that contains the sign-out functionality.
With this all tied together we now have an authentication flow which allows us to easily handle the signed-in and signed-out states of our application. Whilst we have only looked at email authentication, the other authentication methods provided by Firebase operate in similar ways to this. This means that switching to another method will not be much of a change to your implementation.
I hope from this article you’ve been able to see how to setup Firebase Authentication in your application, as well as how to utilise some of it’s functionalities in authentication state. There is plenty more that Firebase Auth has to offer, so it’s definitely worth checking out the full documentation for more info in these areas. If you have any questions or comments then please feel free to reach out!