Firebase is a cross-platform real-time mobile database platform that allows coders to focus on what they do best—coding their apps—without having to worry about DevOps concerns like server infrastructure and database modeling. Backed by Google, Firebase takes the complexities out of dealing with back-end real-time databases, authenticating users, and working with offline synchronization workflows.
While there are many solutions out there for BaaS, such as Realm (check out my Realm.io tutorial here on Envato Tuts+) Firebase doesn’t require any prior infrastructure server configuration, as the platform takes care of hosting and in return exposes an SDK.
Beyond a NoSQL real-time database, with Firebase you get analytics, crash-reporting, user authentication, cloud messaging, push notifications and more. The associated costs also scale with your project—as you grow, you move from a freemium model to a per-usage model.
In this tutorial, I'll show you how to set up Firebase on iOS using CocoaPods, and how to authenticate users using two popular methods: email and password or via the phone with SMS.
To learn about Firebase for Android, check out some of our other tutorials here on Envato Tuts+.
Your First Firebase App
Prerequisites
You will need the following:
Assumed Knowledge
This tutorial assumes you have a working knowledge of iOS and Swift, as well as some basic experience with CocoaPods. If you need to learn more, check out our Swift tutorials and CocoaPods tutorials.
Objectives of This Tutorial
By the end of this tutorial, you'll have started on a simple Firebase-powered app that makes use of the Firebase SDK to authenticate users, using email and password as well as by SMS. Along the way, you'll learn about:
- setting up Firebase with CocoaPods
- setting up the App Delegate to connect to Firebase
- setting up the provisioning entitlements for Text/SMS user authentication
- using FirebaseUI to authenticate users easily
In future tutorials in this series, you will learn to work with other aspects of the Firebase platform, such as using the real-time database to store app data.
Set Up the Project
In this series, we're going to build a to-do app called FirebaseDo. Let’s start by cloning the project from GitHub:
$ git@github.com:tutsplus/get-started-with-firebase-authentication-for-ios.git ... $ git fetch --all --tags ... $ git checkout tags/START
Next we are going to initialize the project to generate a new PodFile, as follows:
pod init
You should see a new file named Podfile located in the root directory of your project. This file basically sets out the libraries we want to use in our project. Open it and add the following Firebase declaration lines:
pod 'FirebaseUI' Pod 'Firebase'
Save and then enter the following in your terminal to build the pods:
pod install
We will be using FirebaseDo.xcworkspace instead of FirebaseDo.xccodeproj, allowing us to work with the dependency libraries we set up on CocoaPods, so go ahead and open the workspace and then switch to your browser.
Now go to the Firebase panel and create a new project:
Next, click on Add Firebase to your iOS app, which will then walk you step by step through the process of registering your app on Firebase.
At some point, it will instruct you to add the GoogleService-info.plist file into your Xcode project:
You've already added the libraries via CocoaPods, so you can skip the remaining instructions and return to the Firebase console.
Authenticating Users
Firebase Authentication provides backend services, easy-to-use SDKs, and ready-made UI libraries to authenticate users to your app. It supports authentication using passwords, phone numbers, popular federated identity providers like Google, Facebook and Twitter, and more. (source: Firebase Authentication)
Before we demonstrate how to use FirebaseUI to automate the authenticating your users, we are going to first explore the SDK methods that Firebase exposes as part of the FirebaseAuth Framework Reference API for handling the creation and sign-in of users manually.
Sign Up, Sign In, and Sign Out Users
To create a new user, you would use the Auth.auth().createUser()
method block, as follows:
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in // ... }
Assuming the error
object is nil, the user will not only be signed up successfully but will also be signed in. To sign in an existing user explicitly, you would call:
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in // ... }
Signing out a user is as easy as calling try!
FirebaseAuth.signOut()
:
do { try firebaseAuth.signOut() } catch let signOutError as NSError { print ("Error signing out: %@", signOutError) }
We want to be able to handle various potential errors gracefully, in case the authentication calls go astray, and when the error
object is not nil (or inversely, the user
object is nil), an error has occurred. Consult the Firebase documentation for a list of all common error codes. In our code, we'll only handle a few general errors.
Managing Authentication State Changes
Throughout the app lifecycle, the authentication state will change, so being able to detect when a user has authenticated, or a session has expired, is important in ensuring users don't have access to parts of your app that they shouldn't be accessing.
By creating a handler, .addStateDidChangeListener
, you are now able to detect what state the user is in and trigger a specific call, based on that.
handle = Auth.auth().addStateDidChangeListener { (auth, user) in // ... }
Managing Users
After the user has authenticated, you are able to access the user
object and obtain user information such as the user’s ID, email address, and photo avatar (if provided). The following method will also assert that the user is indeed currently authenticated with the user
object not being nil:
if Auth.auth().currentUser != nil { // User is signed in. let uid = user.uid let email = user.email let photoURL = user.photoURL // ... } else { // User is not signed in }
Emailing Users
Firebase provides excellent support for sending users email verifications or a password reset request email.
Auth.auth().currentUser?.sendEmailVerification { (error) in // ... } Auth.auth().sendPasswordReset(withEmail: email) { (error) in // ... }
Anonymous Users
Firebase also has a way to manage the anonymous authentication workflow, which essentially is a temporary account that can be used to authenticate users and provide them with limited access. The idea is at a certain point in time, anonymous users might opt to sign up, and Firebase can then provide a bridge to link their anonymous accounts to their sign-in credentials without having to lose data persisted during their anonymous states. To sign in an anonymous user, you would call:
Auth.auth().signInAnonymously() { (user, error) in // ... }
To transition the user from an anonymous account to an authenticated account (using email and password), you would call the following method within a registration screen view controller, requesting the email and password, and then call the user.link()
method.
let credential = EmailAuthProvider.credential(withEmail: email, password: password) user.link(with: credential) { (user, error) in // ... }
Firebase also supports methods for the other federated authentication mechanisms.
There you have it—we’ve gone through the important API methods that Firebase have provided to handle and authenticate users. While the code I've shown you isn't complicated by any means, Firebase makes it even easier than this, with the introduction of FirebaseUI. In this second half of this tutorial, we are going to add authentication to our sample to-do app.
Implementing FirebaseUI
FirebaseUI provides a drop-in auth solution that handles the UI flows for signing in users with email addresses and passwords, phone numbers, and with popular federated identity providers, including Google Sign-In and Facebook Login.
Next, we are going to demonstrate how to implement authentication using FirebaseUI.
In the Firebase Console, go to the Authentication tab and enable the following modes of authentication:
- Email/Password
- Phone
We just saw how to manage and authenticate users manually using the available Firebase SDKs. We are now going to see how to let FirebaseUI do all the heavy lifting for us. In HomeViewController.swift
, import the following libraries:
import UIKit import Firebase import FirebaseAuthUI import FirebasePhoneAuthUI
In the UIViewController.swift
declaration, add the FUIAuthDelegate
:
class HomeViewController: UIViewController, FUIAuthDelegate { ...
Below this class declaration, we are going to declare three private variables that we will be working with. These will let us reference our current authentication object, the AuthUI instance, and our authentication listeners, respectively:
fileprivate(set) var auth:Auth? fileprivate(set) var authUI: FUIAuth? //only set internally but get externally fileprivate(set) var authStateListenerHandle: AuthStateDidChangeListenerHandle?
Next, let's wire up our View Controller so that when it loads up the first time, we hook up a listener to detect when the authentication state changes using a handler. When the auth state changes, we'll summon the self.loginAction(sender: self)
method to bring up our FirebaseUI authentication controller.
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. self.auth = Auth.auth() self.authUI = FUIAuth.defaultAuthUI() self.authUI?.delegate = self self.authUI?.providers = [FUIPhoneAuth(authUI: self.authUI!),] self.authStateListenerHandle = self.auth?.addStateDidChangeListener { (auth, user) in guard user != nil else { self.loginAction(sender: self) return } } }
In this method, we also instantiate the private variables we've declared earlier, set the authUI
delegate to our own class, and finally set the list of other providers we will be supporting, which in our case will be FUIPhoneAuth
.
If we wanted to support Google, Facebook or other third-party providers, we could add them to this list. Also note, we don’t need to explicitly include email and password as it is implicit, provided it is enabled it in the Firebase console.
Showing the Login UI
Next we handle the loginAction()
method, which would be called in the case that the event listener determines the user is not currently authenticated. All we need to do in this case is present the authUI.authViewController
modal FirebaseUI, and it would include the associated authentication providers we declared earlier.
This is where the magic begins, as FirebaseUI handles everything: asking the user to enter his or her email address, determining if the user exists (in which case the user will be asked for their password), or for a new user, collecting their name and nominating a password.
@IBAction func loginAction(sender: AnyObject) { // Present the default login view controller provided by authUI let authViewController = authUI?.authViewController(); self.present(authViewController!, animated: true, completion: nil) }
If we were to implement the authentication methods manually, we would need to handle all these different scenarios, including email password resets and so on.
Handling Auth State Changes
Finally, we implement the required protocol for our FIRAuthUIDelegate
delegate, which will allow us to listen and handle authentication states. This method will only proceed if an error has indeed occurred, but we can even handle successful authentications.
// Implement the required protocol method for FIRAuthUIDelegate func authUI(_ authUI: FUIAuth, didSignInWith user: User?, error: Error?) { guard let authError = error else { return } let errorCode = UInt((authError as NSError).code) switch errorCode { case FUIAuthErrorCode.userCancelledSignIn.rawValue: print("User cancelled sign-in"); break default: let detailedError = (authError as NSError).userInfo[NSUnderlyingErrorKey] ?? authError print("Login error: \((detailedError as! NSError).localizedDescription)"); } }
Phone Authentication Setup
Before we take the app for a spin, we need to add a few more steps to be able to handle phone-based authentication. Phone authentication lets users enter their phone numbers, and verifies their identity via an SMS message that includes a one-time code.
To obtain the APNs token needed by the Firebase server, implement the following in the AppDelegate.swift file:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { // Pass device token to auth Auth.auth().setAPNSToken(deviceToken, type: .prod) }
As we have already enabled the phone number sign-in in the Firebase console, our next task is to provision FirebaseDo to receive APNs from Firebase, a task you would also do if you want to support push notifications in general. In this case, however, Firebase will send a silent push notification to the device to verify the phone number sign-in request.
You won’t be able to test this authentication method via Xcode Simulator, rather you will need to connect your iPhone to install and run the app.
In Xcode, go to Capabilities and enable Push Notifications. The app will automatically provision and create a FirebaseDo.entitlements file, as shown in the project navigator.
Next, we are going to create an Apple Push Notification Authentication Key to upload to Firebase. In the Apple Developer Portal, under Keys, fill in the name of your project, making sure to tick APNs. Download the resulting .p8 file and take note of the key ID as we are going to need to enter it shortly.
Switch back to the Firebase Console, and under Project Settings (the gear icon), select the Cloud Messaging tab. Under iOS App Configuration and APNs Authentication Key, select the Upload button and upload the .p8 file, along with the key ID and app ID. The resulting settings screen should resemble the following:
Testing the App
And that’s it—we didn’t have to add very much extra code to our app to set it up for a complete signup and login authentication workflow. Let’s build and run the app in Xcode to see FirebaseUI in action. The first time you run the app, you won't be authenticated, so you will get a generic template with the authentication options you’ve chosen in Firebase console.
It does look a bit bland, but you can customize almost every aspect of the template.
Entering a new user’s email address will push the Create Account screen, asking you for your name and password.
Completing this form will register you as a new user (although our app will only display a blank screen). To confirm that a new user has been created, you can go to Authentication > Users in your Firebase Console.
Test out each of the authentication methods, remembering that you will need to log-out in order to re-trigger the authentication prompt. Re-trigger auth so by adding the following code as the first line in viewDidLoad()
after the super call:
try firebaseAuth.signOut()
This will force the application back to its initial state so that you can test authenticating via phone SMS. Run the app again, this time choosing Sign in With Phone.
Conclusion
In this tutorial, you got an introduction to using Firebase as a back-end platform for your app, and you saw how to authenticate users using the traditional email and password combination, as well as via phone and SMS, an approach made popular by apps like WhatsApp.
We then started building our simple FirebaseDo app, and although it doesn’t do any of the actual reminder-type functionality yet, we will start working on that in the next tutorial. But in just a few lines, we managed to accomplish the following:
- integrated Firebase to set up an authentication mechanism for email and password
- added the ability to authenticate via SMS
- tracked authentication states
- handled errors gracefully
In the rest of the series, you will get a look at some of the other components of Firebase.
And while you're here, be sure to check out some of our other posts on iOS app development!