Part 2: Add User Management to your Agora Video Demo in iOS

This is a followup tutorial to my intro on how to build a video chatting app. You’ll need the completed project from that tutorial to follow this one. I recommend going through the tutorial yourself, but you can also download the starter project from Github.

Setting Up Firebase

Go to https://console.firebase.google.com and create a new Firebase project. Follow the instructions there to set up Firebase within your existing app. We’re going to be using Firebase for authentication and analytics, so make sure you add the following pods to your Podfile:

pod 'Firebase/Analytics' 
pod 'Firebase/Auth' 
pod 'FirebaseUI'

Once you’ve finished going through Firebase’s setup, you should have completed the following steps:

  1. Register your app’s Bundle ID with Firebase. (As a reminder, you can find your Bundle ID in your project settings, under General)
  2. Download the GoogleService-Info.plist file and add it to your app.
  3. Add the Firebase pods above to your Podfile, and run pod install. (Make sure to close and reopen your .xcworkspace file afterwards)
  4. Import Firebase to your AppDelegate, and call FirebaseApp.configure() in didFinishLaunchingWithOptions.
  5. Run your application and have Firebase verify communication.

You’ll then be presented with the Firebase dashboard. Go to the Develop pane, where you’ll find the Authentication section.

Click on the “Set up sign-in method” button to move to the sign-in method pane. Enable the Email/Password and Google sign-in options. You’ll need to set your public-facing app name and support email to do so.

In Xcode, you’ll need to set up a URL scheme to handle Google sign-in. Copy the REVERSED_CLIENT_ID field from your GoogleService-Info.plist, and open up the URL Types pane in the Info section of your project settings:

Add a new URL type and paste the reversed client ID into the URL Schemes field. We’ll also need to write some code so our app knows how to handle that URL. We’ll be using Firebase UI, so for us it’s as simple as just telling Firebase to handle it. Add the following to your AppDelegate.swift:

There are plenty of other sign-in options that you may want to allow, but we won’t be covering them here. If you have questions about one in particular, drop a line in the comments so I can cover it in the future.

New Views

With the addition of users being able to log in, we’re going to need a few new screens to cover the new functionality. We need a view for searching for other users to call, and a settings page where users can change their display name and log out. We’ll set aside our video view controller for now, but we’ll connect it up again later.

Add a Navigation Controller to your Main.storyboard and set it as the initial view controller. It will come with an attached TableViewController as the root view controller – get rid of that and replace it with a standard View Controller, and add a Search Bar and a Table View to it. Create a prototype cell with a label for the user’s display name and email, and give it the reuse identifier userCell.

Add a bar button item to the navigation bar so the user can access their settings. Add a new view controller for the settings page, and give it a text field for the user’s display name, and a button to save their changes and to log out. Make sure to set the action of the Settings button to show the Settings page.

Finally, create two new custom UIViewController subclasses and a custom UITableViewCell and hook everything up:

Make sure you set the UserSearchViewController as the tableView’s delegate and dataSource.

Logging in with FirebaseUI

In this tutorial, we’ll be using Firebase’s built-in UI to handle sign-in for us. If you already have a login page, or simply want to be more flexible with your UI, you can find the docs for logging in programmatically with email and Google here and here, respectively.

We’re going to be using FirebaseUI to log in to our app. We’ll have our initial entry screen — our UserSearchViewController— handle showing the default FUIAuth View Controller. All we need to do is tell it what providers we want to allow, and who to tell when the user successfully logs in:

But there’s a problem. When do we want to show this page? We could show it on startup, but it would get pretty annoying to have to log in every time we open the app. To solve this, we can use something provided by FirebaseAuth — an AuthStateDidChangeListener. It will tell us whenever the user’s authentication state changes, and allow us to show the login page only if there’s no user already logged in. Adding one is pretty simple:

And there we have it! A functional login page that will appear if the current user is nil. There’s still more Firebase can do for us, though. It’s time to make a user database that we can use to allow our users to call each other.

Creating a User Database

Firebase will track our users for us — you can see this for yourself on the Authentication tab of the Firebase dashboard, after you sign in to your app with a new account. However, this list of users isn’t very useful to us. While we can get information from it about the currently logged-in user, it won’t allow us to get any info about other users. We’ll need our own database for that.

Go to the Database tab on the Firebase dashboard, and create a new Realtime Database. Start it in test mode for now, so we can easily modify it without having to worry about security while we’re working on it. We could add data manually here, but it’ll be easier to do it automatically in code.

Adding Users on Login

Head back to our FUIAuthDelegate extension. We’re going to make use of that didSignInWith callback to add a user to our database whenever they log in:

This code gets a reference to our main database, and adds an entry in a new “users” node. Each child of a node needs to have a unique key, so we use the unique UID Firebase gives us, and we store the user’s email, their display name, and a lowercased version of their display name that will make it easier to search for it later.

Note that this code will overwrite our user node every time the user logs in. If you want to add additional fields to our user database, this code will need to be adjusted so it doesn’t delete things.

Searching for Users

With this new code in our app, log in with a few accounts, and you’ll see new entries appear in the Database tab of the Firebase dashboard. The next step is to allow our users to search for other users. Let’s make that search bar a bit more functional.

First, we need to get a reference to our users table.

Then, we can query that database when the user enters text into the search bar, and store the results in an array.

This code creates a query that searches for a user whose username or email exactly matches the text the user entered. You can use .queryEnding(atValue: text + "\u{f8ff}") to instead search for all entries that match the prefix entered – e.g. searching for “Jo” would return users named “Jon,” “John,” or “Jonathan.”

Now that we have results, it might be helpful to actually have our table display them:

If you run your app and search for another user, they will now appear in your list! Very cool. However, you may also notice Firebase complaining at you in the console:

[Firebase/Database][I-RDB034028] Using an unspecified index. Your data will be downloaded and filtered on the client. Consider adding “.indexOn”: “username” at /users to your security rules for better performance

This is Firebase telling us that it’s not indexing our users by our search fields, because we haven’t told it to. With as few users as we have now, it’s not a big deal, but if we ever want to release to a large userbase, we should fix this. Fortunately, adding the rule is easy. Head to the Database tab in your Firebase dashboard, and open up your Rules. Add the .indexOn field to your users database and hit Publish:

Add Settings

Let’s fill out our Settings View Controller next. First, let’s hook up our log out button. Like in the last screen, we’ll use an AuthStateDidChangeListener to keep track of changes to our auth state. That way, whenever the user object goes away for any reason, we can jump back to the root view controller (which will then show our login page). It also allows us to populate our name field with the user’s current display name.

Then, the log out function becomes extremely simple:

To allow the user to change their display name, we need to create a UserProfileChangeRequest, which will update their data in Firebase Auth. We will also need to update the values in our user database. We do all this work in didTapSave():

Video Calls, Once More

It’s finally time to hook up our video call screen again. We’re going to use a manual segue to do this, so we can pass in some useful data about the user we want to call. To create a manual segue, Control+Click on the User Search View Controller and drag to the Video View Controller, and select ‘Show’.

Make sure to give it an identifier — “startCall” will work.

We’re going to use tableView’s didSelectRowAt and prepareForSegue to make sure our video view controller gets initialized correctly.

And over in our Video controller:

Our prepareForSegue creates a channel that combines the UIDs of the current user and the user that was searched for, alphabetized to make sure both parties end up in the same room. We’ve lost the ability to create group calls, but don’t worry, we’ll be adding that back in… next time. There’s a lot more features to be added to this app, but for now we’ll be stopping here, since we’ve already covered quite a lot.

We now have an app that users can log into, set their display names, search for other users within the app, and then join calls with those users. That’s quite an accomplishment for this week. If you want to expand your interaction with Firebase, their guides can be found here and their docs here.

Happy coding!