How to Build a Drop-in Video Chat Application in iOS

Many cities and states have been under lockdown since the outbreak of the coronavirus epidemic. During this difficult time, we’re all looking for new ways to stay connected and support each other (and not go absolutely insane from the boredom and isolation). This is when social networking applications such as Houseparty become especially relevant and helpful.

These applications let users meet up and have fun with their friends without having to leave their homes. Users can enter their friend’s virtual room by just clicking a button. Houseparty, in particular, also provides some built-in games that users can play together.

If you’ve ever wondered how these cool applications are made, read on! This blog post will help get you started on the basics of building a similar social networking application in iOS.

Prerequisites

  1. A basic understanding of Swift and the iOS SDK.
  2. An Agora.io developer account.
  3. Xcode and an iOS device.
  4. CocoaPods (If you don’t have CocoaPods installed already, you can find instructions here).

Overview

This guide will go over the steps for building a social networking application similar to Houseparty. This is a list of the core features that will be included in our app:

  • Users can create and log into their account. User account information will be saved in Google Firebase Realtime Database.
  • Users can set up virtual rooms to host video calls.
  • Users can configure the accessibility of their virtual rooms. “Public” rooms are open for all friends to join and “private” rooms are invitation-only.
  • During a video call, users can send private messages to another user in the same room by double-clicking on that user’s remote video.
  • Users can chat with friends who are not in the room by clicking a button next to their names in the friend list.

You can find my Github demo app as a reference for this article.

Setting Up a New Project

To start, let’s open up Xcode and create a new, blank project.

  1. Open Xcode and select New Project.
  2. Select Single View App.
  3. Name and finalize your project. Make sure the language is set as Swift.

Setting Up CocoaPods

  1. In Terminal, navigate to the root directory of your project and run pod init to initialize CocoaPods.
  2. Open the Podfile that was created and add the pods for the Agora library, as well as the Firebase libraries we’ll use for user management:

    target ‘Your App’ do
    use_frameworks!pod ‘AgoraRtcEngine_iOS’
    pod ‘AgoraRtm_iOS’
    pod ‘Firebase/Analytics’
    pod ‘Firebase/Auth’
    pod ‘Firebase/Database’
    pod ‘FirebaseUI’end
  3. Run pod install in Terminal to install the libraries.
  4. From now on, open YourApp.xcworkspace to edit and run your app.

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, analytics, and user management.

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. Import Firebase to your AppDelegate, and call FirebaseApp.configure() in didFinishLaunchingWithOptions.
  4. Run your application and have Firebase verify communication.

    You will 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:
URL Schemes

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.

Setting Up the View

Storyboard Layout

In your Xcode project, update the Main.storyboard and add a NavigationController. We’re going to be using a UICollectionView to manage our video streams, so update your root view controller by adding a UICollectionView with a custom UICollectionViewCell. We’re keeping it simple, so all you need in this custom class is a view to show video:

Custom UICollectionViewCell

We’ll also need a bunch of buttons to handle all the user’s actions:

Basic button handlers

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 AgoraVideoViewController — 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:

FirebaseUI Initialization

We could call this function 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 to us by FirebaseAuth — an AuthStateDidChangeListener. It will tell us whenever the user’s authentication state changes, and allow us to only show the login page if there’s no user already logged in. Adding one is pretty simple:

Auth State Listener

We now have a functional login page that will appear if the current user is nil.

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, if you try signing in. 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.

Creating a Realtime Database

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:

Adding Users to the Database

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 lowercase version of their display name that will make it easier to search for it later.

Joining a Video Call

Once the user has logged in, they should automatically be placed into their own personal room so that their friends can drop by. To do that, we’ll be using the Agora SDK to quickly and easily handle video calls for us.

Add Camera and Microphone Permissions

In order to use the microphone and camera, we’ll need to ask the user for permission to do so. In your Info.plist add the following keys:

Privacy — Microphone Usage Description
Privacy — Camera Usage Description

Make sure you add a value for each. These values are user-facing, and will be displayed when the app asks for these permissions from the user.

Initialize the Agora Engine

In order to do anything with Agora, we need an AgoraRtcEngineKit object initialized with our appID (acquired from the Agora Developer Console). Let’s add a helper function that will give us this object when we need it, creating it first if necessary.

Creating the Agora Engine

Enable Video

Next, we need to tell Agora we want to enable the video, and set up the video configuration.

Set Up Video

Feel free to adjust the video configuration to suit your needs. You can view the agora documentation on video configuration here.

Join a Call

We want users to join a call as soon as they log in. To make sure each one is placed into their own personal channel, we’ll use their uid as the channel name.

Update your viewWillAppear and add a new function for joining a call:

Joining a Call

Note: Agora uses UInt IDs to identify individual users within a call. Our Firebase UIDs won’t work for this, because they’re strings, and it’s not important that they stay the same from call to call, so we just pass in 0 and save whatever Agora assigns us for later use.

Displaying Video

It’s finally time to put our UICollectionView to good use. First, we’re going to implement some Agora delegate functions so we can detect when other users join our call, and keep track of their in-call ids.

Setting up user list

And then we need to set up our collection view cells to display video:

Displaying video cells

Note: Make sure you set your view controller as a UICollectionViewDelegateFlowLayout. Learn from my mistakes.

Working with Friends

It’s time to start keeping track of a user’s friends. We’re going to add two popover views to our app — one for searching for users to add as friends, and one for displaying a friends list that we can use to join other user’s rooms.

UserSearchViewController

We need a UISearchBar, and a UITableView to display the results. We’ll make a very simple UserTableViewCell class for our cells:

UserTableViewCell

Make sure to hook everything up to our UserSearchViewController class, and make sure to set the ViewController as the initial view controller. Then let’s show our new View Controller when our user hits the “Add Friends” button. In our main AgoraVideoViewController class:

Showing User Search

We set up some basic stuff when we load:

User Search Setup

Most of our complex logic comes when the user actually searches:

Searching for Users

When the user hits the search button, we perform a database query to return all users whose username starts with the text entered.

Tip: If you don’t have multiple phones to test with, you can always add dummy users into your database directly in the Firebase console.

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 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:

Indexing on Username

Adding a Friend

Finally, we need to add a user to our friends list when the user selects them. To do that, we’re going to create a “friends” database alongside our user database and add a node to it:

Viewing Your Friends

Viewing Your Friends

Now that we have friends on our friends list, it’s time to use that to join other people’s rooms. Let’s make one more view controller.

Join Friend

We need a UITableView and that’s about it. We can even reuse our UserTableViewCell class. Again, we show it in a popover when the user taps a button. This time, though, we’re going to create a protocol for our new view controller to tell our AgoraVideoViewController which friend’s room we want to join.

didTapInvite()

And in our new class:

JoinFriendViewController Setup

When the view appears, we’ll get our usual reference to the current user, and use that to get each friend’s ID:

Create Friends List

And then we display each friend’s username, and pass the ID back to the video view when selected:

Display Friends List

Finally, back in our AgoraVideoViewController, we handle the message and change rooms accordingly.

Locking Rooms

Sometimes, a user wants to set his own virtual room to become a “private” room so that no one else can join his room except those who are already in the room. So how should we achieve that?

First, we’ll need to know whether the current room belongs to us. Then, we’ll allow the host to lock the room with a button, and save that state to the database.

Lock variables

Add the following line to the success callback of joinChannel:

self?.isLocalCall = channelName == self?.currentUser?.uid

And then implement the handler for the button:

Setting the Lock

Our channels can now be locked and that state is saved to the database. But it doesn’t do anything yet. We need to check whether a room is locked when we try to join it. In our JoinFriendViewController let’s first show the user if a room is locked by adding the following to cellForRowAt:

Showing the Lock

Unlike with observeSingleEvent, the observe function will be called whenever the value being observed changes. Which is great for us, because it means the cell will automatically update as soon as any of our friends locks or unlocks their room.

We can then update our didSelectRowAt function to show an alert if the user tries to join a locked room:

Respecting the Lock

And that’s all there is to it!

Adding Text Chat

We’re going to use Agora’s Real-Time Messaging (RTM) SDK in order to allow users to chat with each other while they’re in a video call. First, let’s set up the AgoraRtmKit.

We use the same Agora app ID we already have, and then we login and logout of RTM when our user does. We also set up the AgoraRtmDelegate protocol. We’re only going to use it to make sure our chat button isn’t clickable until we’ve finished logging in.

For the chat room itself, we’ll need one final view controller.

Chat View Controller

We need aUITextField to sen d messages, and a table view to display them. We again have a very simple cell:

Like before, we’re going to present this view as a popover when the user hits the ‘Chat’ button.

Joining a Text Channel

In our ChatViewController, we’re going to do some very familiar setup to make sure we have a user at all times, and then join an RTM channel in viewWillAppear:

The createChannel method will create a channel if one doesn’t exist, or join an existing one if it does. Like with Agora video channels, everyone that joins a channel with the same name will be able to chat with each other.

Showing Messages

In order to display a conversation, we need to do two things: Send messages when the local user types them, and receive messages from everyone else.

Whenever the user sends or receives a message, we add it to our message data, insert a new table row, and then scroll to the bottom to make sure we see it.

Handling the Keyboard

If you try to test the app now, you’ll notice an immediate problem: Our text field is at the bottom of the screen, and the keyboard covers it up when you select it. Let’s fix that.

Here, we add a reference to the NSLayoutConstraint attaching the text field to the bottom of the screen. Using Notification Center, we can then find out when the keyboard is shown or hidden, and adjust how far from the bottom of the screen our text field is automatically.

Finishing Touches

Finally, let’s add the functionality to our final two buttons, both of which are a simple call to an Agora API. To switch the camera:

Switch Camera

And to mute our local audio:

Mute Audio

Congratulations

If you’ve made it this far, congratulations! You have a working social app. The best way to test it is to build it and run on multiple phones, but you can also use Agora’s web demo to substitute as additional users.

Thank you for following along. If you want to see more features such as push notifications or presence tracking, please leave a comment below! If you have any questions, you can also reach out via email at devrel@agora.io.


Want to build Real-Time Engagement apps?

Get started with 10,000 free minutes today!

If you have questions, please call us at 408-879-5885. We’d be happy to help you add voice or video chat, streaming and messaging into your apps.