How to build an audio app like Clubhouse for iOS

100ms
8 min readOct 21, 2021

--

The face of video/audio communication has changed dramatically due to the global pandemic. As corporations consider video/audio conferencing as an ultimate solution to connect with remote workers/customers, the affinity to provide features has gone up.

Building a feature-rich Video/audio conferencing application isn’t that easy, and this is where 100ms comes to the rescue. In this guide, we will be building a Clubhouse clone using 100ms iOS SDK.

Getting Started

Our Clubhouse clone will have the following features:

  • Audio Call
  • Raise hand
  • Move people who raised a hand to speakers and back to the audience

To focus on SDK integration rather than room list management, the app will use a single predefined room.

*Join our upcoming workshop ‘ Build 👋 Clubhouse like app for iOS in 45 minutes’ — https://community.100ms.live/developer-community-meetup-october

Terminology

  • Room - A room is a primary object that 100ms SDKs return on successful connection. This contains references to peers, tracks, and everything you need to render a live a/v app.
  • Peer - A peer is an object returned by 100ms SDKs that contains all information about a user - name, role, video track, etc.
  • Track - A track represents either the audio or video that a peer is publishing
  • Role - A role defines who a peer can see/hear, the quality they publish their video, whether they have permission to publish video/screen share, mute someone, or change someone's role.

Prerequisites

To follow this tutorial, you must have a basic understanding of the elementary principles of iOS Development, Xcode, Swift.

Setting up Project

To simplify things and help us focus on adding the core functionality, I already created a template project with the main UI for the audio room app ready for the SDK integration.

Now that you have cloned the repo, you will find two folders inside:

  • AudioRoomTemplate — some boilerplate project if you want to go along with this guide step by step
  • AudioRoomComplete — if you’d instead explore the finished project.

Dependencies

Open AudioRoom.xcodeproj from the AudioRoomTemplate folder. Our first step would be adding 100ms SDK to the project. For that we will be using Swift package manager. Select "Add Package" from Xcode File menu and use https://github.com/100mslive/100ms-ios-sdk.git as the package url.

Access Credentials

The primary means of authentication for 100ms SDK is a jwt auth token. It is never a good idea to hardcode tokens in your app, so your backend should provide this token. You might not have a backend set up during prototyping, so 100ms provides a temporary token generation backend for you to try, which should not be used in production. To get a token from the 100ms backend, we need a token endpoint URL and a room id.

To get these credentials, you first need to create an account at 100ms Dashboard. After your account is set up, head over to the Developer Section, and you can find your Token endpoint URL there.

Creating Roles

Before we create a room, we will create a custom app; you can find it here. Click on “Add a new App.” When you are asked to choose a template, select “Create your Own”.

Now click on “Create Roles” button this will open a modal were we can create our custom roles.

We will be having 4 roles in our application.

  • audience - only able to listen to others, can change role to speakerwannabe
  • speakerwannabe - same as speaker, will be used to segregate people who raised their hand
  • speaker - able to listen and speak
  • host - everything that speaker can plus the ability to change other’s role

We will create host role first. Input host into the role name field, leave only "Can share audio" enabled under "Publish Strategies". Under permissions enable "Can change any participant's role" and then click "save".

Proceed to create speaker role which should be identical to host. Now lets create audience and speakerwannabe roles. Disable everything under "Publish Strategies". Under permissions enable "Can change any participant's role" and then click "save".

Now that our roles are setup we will move on to our next step by clicking ‘Set up App’. You should see your custom app being created.

Creating a Room

In the last step we created our custom app and setup custom roles , in this step we will create a room id.

To create a room head over to Create Room page in 100ms Dashboard click on “Create Room” make sure to select the Custom App that you created in the last step. Fill out Room Name and click “Create Room”. After creating you will be routed to Room Details were you can find your room id.

Awesome! Now that we have token endpoint and room id we will add it in our app. Open TokenProvider.swift file and insert the values in the Constants struct. It should look like this:

Now we can start building our project.

Joining The Room

The initial screen of the app is the login screen controlled by the The initial screen of the app is the login screen controlled by the MainViewController. When the user taps the “JOIN ROOM” button, it uses TokenProvider class to fetch the token from the 100ms token backend and then passes it to RoomViewController, which it then presents modally. Having a token, we can proceed with joining the room. Open RoomViewController.swift file to start. First, you need to add an import for the SDK:

The next thing we need to do is to have an instance of the SDK. It serves as an entry point to everything we will be using.

Now we are ready to join a room. In the join function stub add following code:

The above code creates a configuration for the join call from the user name and token that we have obtained earlier. The join function will be called fro the viewDidLoad override. With the join call we also provide a delegate that will receive the important updates from the SDK. So lets add conformance to the HMSUpdateListener protocol:

The only thing we are going to handle at this stage is on(error:) which will let us see if something went wrong. Rest can be left as is.

Now when user decides to leave the room we should call leave function. This will let other people know we left. The appropriate place to do that in case of a modally presented controller should be endAppearanceTransition override:

With this in place quite a lot is achieved. You can join the room as host/audience/speaker. Audience will not be able to speak but host/speaker will. This is done by the SDK automatically publishing/subscribing to tracks based on the roles configuration we made in the beginning.

Showing Peers In The Room

Now that we can join in the room, let’s display the peers who have joined the room. To get all peers, we will use hms. room?.peers property. This will return us an array of all peers in the room.

Each peer object stores the details of individual participants in the room. You can refer to the interface of HMSPeer in our API-reference docs.

We will show these peers in a UICollectionView separated into two sections: audience and speakers. The host will also have the ability see peers who have raised a hand in the “Raised hand” section. We need to create a data model for the sections to use in the collection view data source to achieve that. The template project already has a Section class that can hold a list of peers and a type of section and logic to tell which role goes into which section. So let us create our data model in the RoomViewController class:

Now that we have added logic to build the model we need to wire it up to the SDK updates so that it gets updated when any of these updates happen:

  • Room is joined and we get the initial list of peers in the room
  • Any peer joins or leaves
  • Peer changes its role
  • Peer mutes/unmutes (in case he is a speaker/host)

With all this in place we can now connect our model to the collection view. The template logic already has a ParticipantCollectionViewCell class that we will use to show our participants.

And now we are able to see who is in the room, wether they belong to audience or speakers and their mic mute status.

Showing Active Speakers

Another must have feature of an audio room app is showing who is currently speaking. This is quite easy to add. First create a storage for the active speaker ids:

Then populate it from on(updated:) callback

Now in the cell configuration add this:

And we are done! The speaker will now be marked by a blue glow.

Mute/Unmute

Now let’s add a mic mute button. First we need a helper function to tell us if we can speak:

Then we will add a function to setup buttons visibility according to role:

This should be called in on(join:) because at that time we will know what role we joined as:

Finally add a mute button tap handler:

The code above queries local peer for its audio track and then sets its mute status. Then reloads model so that our cell can reflect the change. Now joining as a host you will see the mute button. While joining as audience you will see none.

Raise Hand

What makes audio rooms fun is that anyone can become a speaker at some point. To show interest in becoming a speaker we will add a raise hand button. The UI is already in place we just need to unhide it according to role just like with mute button.

Now in the button tap handler we will use changeRole api to change our own role to speakerwannabe so that the host can see us:

Now as a host we should be able to move people who raised hand to speakers, as well as move speakers back to audience. Lets do that by adding showing an action sheet when cell has been tapped:

And we are done. Launch the app and try for yourself!

--

--

100ms

Video conferencing infrastructure for a video-first world. Quick to integrate native iOS, Android & Web SDKs to add live video & audio conferencing to your apps