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.
- A basic understanding of Swift and the iOS SDK.
- An Agora developer account.
- Xcode and an iOS device.
- CocoaPods (If you don’t have CocoaPods installed already, you can find instructions here).
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.
- Open Xcode and select New Project.
- Select Single View App.
- Name and finalize your project. Make sure the language is set as Swift.
Setting Up CocoaPods
- In Terminal, navigate to the root directory of your project and run
podinitto initialize CocoaPods.
- 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
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:
- Register your app’s Bundle ID with Firebase. (As a reminder, you can find your Bundle ID in your project settings, under General.)
- Download the
GoogleService-Info.plistfile and add it to your app.
- Import Firebase to your AppDelegate, and call
- 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_IDfield 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
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
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:
We’ll also need a bunch of buttons to handle all the user’s actions:
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:
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:
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.
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 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.
Next, we need to tell Agora we want to enable the video, and set up the video configuration.
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.
viewWillAppear and add a new function for 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.
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.
And then we need to set up our collection view cells to display video:
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.
We need a
UISearchBar, and a
UITableView to display the results. We’ll make a very simple
UserTableViewCell class for our cells:
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
We set up some basic stuff when we load:
Most of our complex logic comes when the user actually searches:
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:
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
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.
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.
And in our new class:
When the view appears, we’ll get our usual reference to the current user, and use that to get each friend’s ID:
And then we display each friend’s username, and pass the ID back to the video view when selected:
Finally, back in our
AgoraVideoViewController, we handle the message and change rooms accordingly.
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.
Add the following line to the success callback of
self?.isLocalCall = channelName == self?.currentUser?.uid
And then implement the handler for the button:
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
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:
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
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.
We need a
UITextField 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
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
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.
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.
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:
And to mute our local audio:
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 firstname.lastname@example.org.