Creating an app for video calls with QuickBlox React Native SDK
In this article, we will build a React Native application that provides an ability to make video calls using quickblox-react-native-sdk.
You can find a full code sample in quickblox-react-native-samples
Creating application backbone
React Native CLI provides us an easy way to create a new React Native application:
Once react-native-cli created a project we should update `ios/Podfile`:
platform :ios, ‘10.0’. Since QuickBlox React Native SDK has minimum supported iOS version 10.0
We will need the following packages in addition to preinstalled:
- quickblox-react-native-sdk – main package of this application
- redux – to maintain application state
- react-redux – react bindings for redux
- redux-persist – to keep application state persistent across reloads
- @react-native-community/async-storage – as a storage engine for redux-persist
- redux-logger – helper that will put all actions and store changes in debugger’s console
- redux-saga – library for making application side effects (i.e. asynchronous things like data fetching and impure things like accessing the cache)
- react-navigation – routing and navigation for React Native apps
- react-native-reanimated – dependency of react-navigation
- react-native-gesture-handler – dependency of react-navigation
- react-native-screens – dependency of react-navigation
- final-form – form state management library
- react-final-form – form state management for React
- react-native-incall-manager – handling media-routes/sensors/events during a audio/video chat on React Native
- react-native-flash-message – flashbar and top notification alert utility
To install all the packages, run:
We will use separate folders for components, containers, sagas, and other parts of the application:
- actionCreators – application’s action creators
- components – presentational components
- constants – constants representing actions names
- containers – container components
- reducers – application’s reducers
- sagas – sagas (API calls, calls to SDK, etc.)
- store – redux store initialization
- images – key-value images collection used in this application (imported with require, so can be used as Image.source)
- QBConfig – object with credentials for QuickBlox SDK initialization
- theme – app-wide styles (colors, navigation header styles, etc.)
QuickBlox application credentials
In order to use QuickBlox SDK, we should initialize it with correct application credentials. To create an application you will need an account – you can register at https://admin.quickblox.com/signup or login if you already have one. Create your QuickBlox app and obtain app credentials. These credentials will be used to identify your app.
All users within the same QuickBlox app can communicate by chat or video chat with each other, across all platforms – iOS, Android, Web, etc.
In this app, we will store QuickBlox application credentials in file src/QBConfig.js. So once you have app credentials put them into that file:
Our application has several points that we should configure. Let’s move over them:
- constants – this folder will have only one file (index.js) that will export string constants which we will use in this app (check it out in repository)
- actionCreators – this folder will have several files for different app parts and one file exporting all action creators (check it out in repository)
- reducers – this folder will have several files for different app parts and one file that exports all reducers combined AKA root reducer (check it out in repository)
- sagas – this folder will have several files for different app parts and one file that exports all application sagas combined into one saga AKA root saga (check it out in repository)
- store – this folder will have only one file (index.js) that will export function to set up redux store for application (check it out in repository)
Once that done we can configure our entry point at src/index.js:
Before starting our app we should also setup routing / navigation. Here is the code we will use (check it out in a repository):
There is also a logic behind deciding should we use StackNavigator or SwitchNavigator – when the application starts it will display a route that will check if the user authenticated. If not – The Login screen will be displayed. Otherwise, we can route the user to the application. But then we should check if we have a connection to chat and connect if not connected. Then we can route the user further: if there is a WebRTC session – route to CallScreen, otherwise to the main screen.
Now that we have navigation, store and other things set up we can run our app. Let’s update src/App.js to display our router:
Our application is using QuickBlox React Native SDK for audio/video calls. In order to use it, we should initialize it. So let’s update src/App.js and add SDK initialization upon app starts:
When appStart action creator will fire APP_START action the saga will be triggered (in src/sagas/app.js) that will initialize QuickBlox SDK with action payload:
Creating Login form
The first thing the user will see in this app will be the Login form. Let’s create a component for it:
In this code, we have validation of provided login and username, and if validation passed – trying to sign in. After successful sign-in, the “CHAT_CONNECT_AND_SUBSCRIBE” action is successfully dispatched, which in turn triggers connectAndSubscribe saga:
Find the source code of these sagas in the repository.
What is going on here?
Saga checks if there is a user in-store (if the user authorized). If there is no user – do nothing. If the user is authorized – call “isChatConnected” saga. It calls “isConnected” method of QuickBlox SDK (chat module) to understand if we are connected to chat. QuickBlox SDK needs to be connected to the chat for audio/video calling because the chat module is used as signaling transport. If we are not connected to chat and corresponding flag in store does not indicate that we are trying to connect right now – initiate connecting to chat.
Also, initialize the webrtc module of QuickBlox SDK – this is an important part if you want to use audio/video calling functionality.
Then redirect to “Users” route.
Creating Users list
React Native provides several APIs to render lists and we are not building here something extraordinary, so to render list of users we are going to use FlatList:
When the component will mount, it will dispatch the action that will trigger users saga which in turn will call QuickBlox SDK to load users:
Creating call screen
Call screen should work in case a user initiates a call, but also when the user receives a call request. Also, there can be an audio or video call. Let’s say that for audio call we will display circles for opponents (excluding current user) showing opponent name and peer connection status:
And for the video call, we may show WebRTCView from QuickBlox React Native SDK:
You can find the full code of this component (and container, and other parts) in the repository.
Select users and initiate a call
To start a call, we should select the users whom we will call. This is up to you how to implement the mechanism of user selecting, we will focus here on initiating a call when the users are already selected.
In this example, we are showing separate buttons for audio and video call which are disabled if there is less than 1 or more than 3 selected users. When pressing the button, the action is dispatched with opponents-IDs (array containing IDs of selected users) and type of call to start. Here is the saga listening for this action:
Once we created a WebRTC session (started a call), we can navigate to a call screen and wait for opponents’ responses.
Listening to QuickBlox SDK events
QuickBlox SDK sends events from native code to JS when something happens. In order to receive these events, we should create an emitter from QuickBlox SDK module. Net every module of QuickBlox SDK sends events. To find out if the module is sending events you can check if that module has EVENT_TYPE property. For example, check the output of following code in React Native app:
Once we create an emitter from QuickBlox SDK module we can assign an event handler(s) to listen and process events. With redux-saga we can use eventChannel factory to create a channel for events.
To start reading events from chatConnection channel and webrtc channel, we can use sagas that will create channels upon login and will close channel(s) upon logout:
You can find full code at the sample repository.
So if we want to add special hander for some specific event(s) we can either put that logic in
readWebRTCEvents saga or create separate event channel for that event(s). Let’s add some more logic into existing saga:
There isn’t a lot of possible actions we can perform with a call, but let’s go through them to make it clear:
- Accept – can be applied to incoming call requests. To accept a call (session) we should specify the session ID we want to accept
- Reject – can be applied to incoming call request. To reject (decline) call request we should specify the session ID we want to reject
- Hang up – end a call. The same as for examples above – we should specify the session ID we want to end
Once you complete all the steps of this guide, you will get a fully-functional chatting application:
Frequently asked questions
Q: I want to accept a call. Where can I get a session Id to pass in
A: When you create a session or receive a call request (event) QuickBlox SDK returns a session information including id, session type (audio/video) and other information.
Q: I am initiating a call but it seems that my opponent is not online and not sending nor accept, not reject. How long will my device be sending a call request?
A: If there is no answer to call request within 60 seconds, QuickBlox SDK will close the session and send you the event “@QB/NOT_ANSWER” (QB.webrtc.EVENT_TYPE.NOT_ANSWER), meaning that there was no answer from the opponent(s).