Expo Quickstart Guide

Overview

This guide will walk you through simple instructions to create a Video Conferencing app using the 100ms React Native SDK and Expo.

PackageVersion
@100mslive/react-native-hmsnpm

Create a Sample app

This section contains instructions to create Expo App.

Prerequisites

Create a Expo app

Once you have the prerequisites, follow the steps below to create a Expo app.

  1. Open your Terminal and navigate to directory/folder where you would like to create your app.

  2. Run the below command to create Expo app:

npx create-expo-app my-expo-app && cd my-expo-app
  1. Install expo-dev-client library. By installing this library, we will be able to install native libraries and modify project configuration.
npx expo install expo-dev-client
  1. Test run your app
npx expo run:android

The above commands compile your project, using our locally installed Android SDK, into a debug build of your app. Learn more about running android app locally here

Install 100ms React Native SDK

After the Test run of your app is successful, you can install 100ms React Native SDK package in your app.

npx expo install @100mslive/react-native-hms

Refer SDK Implementation Diagram

Below is the diagram which shows the 100msz SDK lifecycle. We start with requesting the Camera & Microphone permissions from the user.

HMSSDK Integration Lifecycle

Now we will look at code for each step in the implementation lifecycle.

Request Camera and Microphone Permissions

We need to request camera and microphone permissions from user before initializing SDK or joining a meeting Room.

  1. Install expo-camera library, It helps us to request Camera and Microphone permissions from user.
npx expo install expo-camera

You can learn more about this library and its usage here.

  1. Add below expo-camera plugin config in your expo plugin file that is root level app.json file
{ "expo": { "plugins": [ [ "expo-camera", { "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera", "microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone", "recordAudioAndroid": true } ] ] } }
  1. Android platform requires some additional permissions. Add below permissions inside android in app.json config -
{ "expo": { ... "android": { ...
"permissions": [
"android.permission.ACCESS_NETWORK_STATE",
"android.permission.CHANGE_NETWORK_STATE",
"android.permission.MODIFY_AUDIO_SETTINGS",
"android.permission.BLUETOOTH",
"android.permission.BLUETOOTH_CONNECT"
]
} ... } }
  1. Now we can request Camera and Microphone usage permissions from user using the following -
// Import required methods from `expo-camera` library import { requestCameraPermissionsAsync, requestMicrophonePermissionsAsync, } from "expo-camera"; ... // We use `requestMicrophonePermissionsAsync` method to request microhone usage permission const microphonePermissionResult = await requestMicrophonePermissionsAsync(); // We use `requestCameraPermissionsAsync` method to request camera usage permission const cameraPermissionResult = await requestCameraPermissionsAsync();

Initialize 100ms SDK

Now that we have Camera & Microphone usage permission, we can initialize 100ms React Native SDK instance using the static build method available on HMSSDK class -

// Import HMSSDK class import { HMSSDK } from "@100mslive/react-native-hms"; ... // `build` method returns an instance of `HMSSDK` class const hmsInstance = await HMSSDK.build();

To know more about Initializing the SDK, HMSSDK.build method and the arguments it accepts, refer to HMSSDK Constructor guide.

Add Event Listeners

The 100ms SDK emits events during Room joining and when the user joins or is in preview. Subscribing to these events helps us understand what is happening at any given time and you show appropriate UI to the user.

We can subscribe to the events emitted by the SDK using hmsInstance.addEventListener method. It accepts name of the event to subscribe to (variant of HMSUpdateListenerActions enum) as first pameter and and callback function as second parameter.

  • The HMSUpdateListenerActions.ON_JOIN event is emitted when the user (you / localpeer) has successfully joined the Room.
// Subscribing to ON_JOIN update hmsInstance.addEventListener( HMSUpdateListenerActions.ON_JOIN, // event name (data) => { const roomObject = data.room; // room object representing the joined room const localPeer = data.room.localPeer; // the local peer object // What to do when this event is received? // When the callback method subscribed to the `ON_JOIN` method is invoked, // it indicates that the current user has successfully joined the meeting/room. // You can now display the meeting/room UI, including video tiles of remote and local peers, // using the data obtained from this event and other room events. } );
  • The HMSUpdateListenerActions.ON_PEER_UPDATE event is emitted when any update occurs for any Peer (users in the room) in the Room, such as HMSPeerUpdate.PEER_JOINED, HMSPeerUpdate.PEER_LEFT, or HMSPeerUpdate.NAME_CHANGED, etc. Refer to the HMSPeerUpdate enum to learn about all available update types in the ON_PEER_UPDATE event.
export enum HMSPeerUpdate { PEER_JOINED = 'PEER_JOINED', PEER_LEFT = 'PEER_LEFT', METADATA_CHANGED = 'METADATA_CHANGED', // Peer metadata changed ROLE_CHANGED = 'ROLE_CHANGED', // Role of Peer changed NAME_CHANGED = 'NAME_CHANGED', // Name of Peer changed NETWORK_QUALITY_UPDATED = 'NETWORK_QUALITY_UPDATED', // Network quaity of Peer changed HAND_RAISED_CHANGED = 'HAND_RAISED_CHANGED', // Peer Raised Hand } ... // Subscribing to Peer updates hmsInstance.addEventListener( HMSUpdateListenerActions.ON_PEER_UPDATE, // event name (data) => { // Updated Peer object const peerObject = data.peer; // peer update type such as `HMSPeerUpdate.PEER_JOINED`, `HMSPeerUpdate.PEER_LEFT`, etc. const peerUpdateType = data.type; // What to do when this event is received? // When the callback method subscribed to the `ON_PEER_UPDATE` method is invoked, // it indicates that there is an update on a peer in the room. // You can check value of `data.type` to know the exact update type. // // You can store the received `data.peer` object // or update the existing object of same peer (use peer.peerId for comparison) // in your app state to reflect the current update in UI }, );
  • The HMSUpdateListenerActions.ON_TRACK_UPDATE event is emitted when any update occurs for any Track (audio, video, screen share) in the Room, such as HMSTrackUpdate.TRACK_ADDED, HMSTrackUpdate.TRACK_REMOVED, or HMSTrackUpdate.TRACK_MUTED, etc. Refer to the HMSTrackUpdate enum to learn about all available update types in the ON_TRACK_UPDATE event.
export enum HMSTrackUpdate { TRACK_ADDED = 'TRACK_ADDED', // new video/audio track added TRACK_REMOVED = 'TRACK_REMOVED', // video/audio track removed TRACK_MUTED = 'TRACK_MUTED', // video/audio track muted TRACK_UNMUTED = 'TRACK_UNMUTED', // video/audio track unmuted TRACK_DESCRIPTION_CHANGED = 'TRACK_DESCRIPTION_CHANGED', TRACK_DEGRADED = 'TRACK_DEGRADED', // video/audio track degraded due to your or remote peer's poor connection TRACK_RESTORED = 'TRACK_RESTORED', // video/audio track restored which was earlier degraded } ... // Subscribing to Track updates hmsInstance.addEventListener( HMSUpdateListenerActions.ON_TRACK_UPDATE, // event name (data) => { // Peer object whose track has an update const peerObject = data.peer; // Updated Video/Audio Track object const trackObject = data.track; // track update type such as `HMSTrackUpdate.TRACK_ADDED`, `HMSTrackUpdate.TRACK_MUTED`, etc. const peerUpdateType = data.type; // What to do when this event is received? // When the callback method subscribed to the `ON_TRACK_UPDATE` method is invoked, // it indicates that there is an update on a audio/video track in the room. // You can check value of `data.type` to know the exact update type. // // You can store the received `data.track` and `data.peer` object // or update the existing objects of same peer (use peer.peerID for comparison) // in your app state to reflect the current update in UI }, );
  • The HMSUpdateListenerActions.ON_ERROR event is emitted when SDK encounters either a critical or a standard error. Error contains a code and message due to which error occoured. Refer to Error Handling guide to learn more about error handling.
// Subscribing to Errors hmsInstance.addEventListener( HMSUpdateListenerActions.ON_ERROR, // event name (error) => { const terminalErrorCodes = [4005, 1003, 2000, '4005', '1003', '2000']; const isTerminalError = error.isTerminal || // check is `isTerminal` property is set to `true` terminalErrorCodes.includes(error.code); // error code is one of above terminal codes // What to do when this event is received? // When the callback method subscribed to the `ON_ERROR` method is invoked, // it indicates that SDK encountered an error (HMSException) // this error can be terminal or normal error // // When error is terminal, you (user) should leave the room and rejoin // If it is normal, you can just log, alert, or show it in a toast message // // 4005, 1003, 2000 are codes for a terminal error } );

Check the Event Listeners guide to learn more about the events emitted by the SDK.

Getting Authentication Token

Authentication token is required to join a 100ms Room. You can learn more about authentication tokens and room code in Authentication and Tokens and Auth Token Quickstart guides.

We can get Authentication Token for joining a room by following ways -

  1. Use room-code to generate Authentication token programmatically (Recommended)

You can get room-code of your room from 100ms dashboard by following Room Code Authentication guide

When you have room code, you can use hmsInstance.getAuthTokenByRoomCode method to generate Authentication Token for joining the room -

hmsInstance.getAuthTokenByRoomCode method accepts roomCode as first parameter, userId & endpoint as optional parameters

const roomCode = 'YOUR_ROOM_CODE'; // Room Code that you got from 100ms dashboard
// `getAuthTokenByRoomCode` returns a promise which is resolved with "auth token"
const authToken = await hmsInstance.getAuthTokenByRoomCode(roomCode);

You can learn more about getAuthTokenByRoomCode method here

  1. Get temporary auth tokens from the dashboard
  • Navigate to your 100ms dashboard or create an account if you don't have one.
  • Use the Video Conferencing Starter Kit to create a room with a default template assigned to it to test this app quickly.
  • Go to the Rooms page in your dashboard, click on the Room Id of the room you created above, and click on the Join Room button on the top right.
  • You will see 100ms demo URLs for the roles created when you deployed the starter kit; you can click on the 'key' icon to copy the token and update the AUTH_TOKEN variable in App.js file.

    Token from 100ms dashboard is for testing purposes only, For production applications you must generate tokens on your own server. Refer to the Management Token section in Authentication and Tokens guide for more information.


  1. Auth Token Server - Programmatically generate Auth Tokens by setting up an Auth Token server.

Join Room

After adding event listeners to receive room updates and getting Auth Token, We can now continue to join the room using hmsInstance.join method.

hmsInstance.join method accepts HMSConfig object as parameter. Lets create an instance of HMSConfig class -

// Import `HMSConfig` class import { HMSConfig } from '@100mslive/react-native-hms'; ... const hmsConfig = new HMSConfig({ // The Client side Authentication Token that we generated from above step, Refer {@link #getting-authentication-token} authToken: token, // The name of the current peer, which should be displayed to other peers in the room. username: storeState.user.userName, });

Now we can use instance of HMSConfig class to call hmsInstance.join method -

const hmsConfig = new HMSConfig({ authToken: token, username: storeState.user.userName, });
hmsInstance.join(hmsConfig);

After you have called the hmsInstance.join method, if everything goes correctly, the SDK will emit the HMSUpdateListenerActions.ON_JOIN event, indicating that the join method call was successful and user has joined the Room.

You can learn more about Joining the Room from Join Room guide.

You can also preview your audio and video tracks before joining the room. Read Preview guide to learn about preview.

Render Video

After joining a room, you can display the videos of peers who are publishing video.

The 100ms React Native SDK provides the HmsView component for rendering video tracks of peers. You can access the HmsView component through the HMSSDK class instance e.g. hmsInstance.HmsView.

The HmsView component requires the id of the video track to render the video. You can use the trackId prop on the HmsView component for this purpose.

To learn how to get the trackId of the video track, refer to the HMSUpdateListenerActions.ON_TRACK_UPDATE event section in the "Add Event Listeners" step above.

// Getting `HmsView` component from `HMSSDK` class instance
const HmsView = hmsInstance.HmsView;
const videoTrackId = '.....'; // `trackId` of video track that we got from `HMSUpdateListenerActions.ON_TRACK_UPDATE` event // Using `HmsView` component to render video <HmsView // `trackId` of video track that we got from `HMSUpdateListenerActions.ON_TRACK_UPDATE` event trackId={videoTrackId} style={{ flex: 1, }} />

You can learn more about rendering video and HmsView component in Render Video Overview guide.

Mute and Unmute Tracks

You can mute and unmute audio and video tracks. Muting audio means that others cannot hear you, while muting video stops your video from being broadcast to others.

To mute and unmute, first you need to obtain the local peer object, which is an instance of the HMSLocalPeer class.

You can get the local peer object from the data received in HMSUpdateListenerActions.ON_JOIN event. You can also get the updated local peer object by calling hmsInstance.getLocalPeer method -

// 1. Getting local peer object in `ON_JOIN` event hmsInstance.addEventListener(HMSUpdateListenerActions.ON_JOIN, (data) => { // local peer object (instance of `HMSLocalPeer` class) const localPeer = data.room.localPeer; }); // ----- OR ----- // 2. Getting local peer object by calling `getLocalPeer` method const localPeer = await hmsInstance.getLocalPeer();

Once you have the local peer object, You can use the localAudioTrack and localVideoTrack methods available on the local peer object to obtain instances of the HMSLocalAudioTrack and HMSLocalVideoTrack classes, respectively.

Then, You can use the setMute method, available on the HMSLocalAudioTrack and HMSLocalVideoTrack classes, to mute and unmute your audio and video tracks, respectively.

Example of Unmuting Audio track of local peer -

// Step 1 - Getting Audio track of local peer const localAudioTrack = localPeer.localAudioTrack(); // Step 2 - using `setMute` method on above `HMSLocalAudioTrack` to mute and unmute if (localAudioTrack) { localAudioTrack.setMute(false); // use `true` to mute }

Example of Unmuting Video track of local peer -

// Step 1 - Getting Video track of local peer const localVideoTrack = localPeer.localVideoTrack(); // Step 2 - using `setMute` method on above `HMSLocalVideoTrack` to mute and unmute if (localVideoTrack) { localVideoTrack.setMute(false); // use `true` to mute }

How to check if a track is muted or unmuted?

You can check if a remote peer's track is muted by calling isMute method available on the HMSTrack class. You receive instance of HMSTrack class in the data received in HMSUpdateListenerActions.ON_TRACK_UPDATE event. Refer to the HMSUpdateListenerActions.ON_TRACK_UPDATE event section in the "Add Event Listeners" step above.

// Subscribing to Track updates hmsInstance.addEventListener( HMSUpdateListenerActions.ON_TRACK_UPDATE (data) => { // `data.track` is instance of `HMSTrack` class // Save this track object in your application state const trackObject = data.track; // Check if track is muted or not const isTrackMuted = trackObject.isMute(); // `true` if muted, `false` otherwise }, );

You also receive HMSTrackUpdate.TRACK_MUTED and HMSTrackUpdate.TRACK_UNMUTED update types when peers mute or unmute their tracks, respectively.

// Subscribing to Track updates hmsInstance.addEventListener( HMSUpdateListenerActions.ON_TRACK_UPDATE (data) => { // Check update type to know if `data.peer` muted their `data.track` const updateType = data.type; if (updateType === HMSTrackUpdate.TRACK_MUTED) { // peer muted their `data.track` } else if (updateType === HMSTrackUpdate.TRACK_UNMUTED) { // peer unmuted their `data.track` } else { // Handle other update types } }, );

You can check if your local peer's track is muted by calling the isMute method, available on the HMSLocalAudioTrack and HMSLocalVideoTrack classes -

// Getting Audio track of local peer const localAudioTrack = localPeer.localAudioTrack(); // Checking if local peers audio track is muted if (localAudioTrack) { const isAudioTrackMuted = localAudioTrack.isMute(); // `true` if muted, `false` otherwise } // Getting Video track of local peer const localVideoTrack = localPeer.localVideoTrack(); // Checking if local peers audio track is muted if (localAudioTrack) { const isVideoTrackMuted = localVideoTrack.isMute(); // `true` if muted, `false` otherwise }

You can learn more about Muting and Unmuting tracks in Mute & Unmute guide.

Leave Room

You can call leave method available on HMSSDK class to leave the room.

leave method returns a promise which is resolved upon completion of leave process.

try { await hmsInstance.leave(); console.log('Leave Success'); } catch (error) { console.log('Leave Error: ', error); }

Free device resources

You should free device resources by calling destroy method available on HMSSDK class after leaving the meeting.

try { await hmsInstance.leave(); console.log('Leave Success'); // Free device resources after local peer has left the room await hmsInstance.destroy(); console.log('Destroy Success'); } catch (error) { console.log('Leave Error: ', error); }

You can read Leave Room guide and Release Resources to know more.

Remove Event Listeners

After leaving the room, You can remove event listeners that you added to receive updates from the SDK.

You can unsubscribe from a specific event using the removeEventListener method available on HMSSDK class, Which accepts the name of the event to unsubscribe from (a variant of the HMSUpdateListenerActions enum).

Here's an example of how to use the hmsInstance.removeEventListener method:

In the snippet below, when the useEffect cleanup function runs, we unsubscribe from the ON_ROOM_UPDATE event.

useEffect(() => { // Subscribing to `ON_ROOM_UPDATE` event hmsInstance.addEventListener(HMSUpdateListenerActions.ON_ROOM_UPDATE, () => { // `ON_ROOM_UPDATE` event listener }); return () => { // Unsubscribing from `ON_ROOM_UPDATE` event hmsInstance.removeEventListener(HMSUpdateListenerActions.ON_ROOM_UPDATE); }; }, [hmsInstance]);

Complete App Code

So far, we've covered the core concepts of the 100ms React Native SDK necessary for integrating it into your application.

Below is a complete code sample for a working video conferencing app that incorporates these concepts.

Replace the code in your App.js file with the following:

import { StatusBar } from "expo-status-bar"; import React, { useState, useRef, useEffect, useCallback } from "react"; import { SafeAreaView, FlatList, Text, View, TouchableHighlight, ActivityIndicator, Alert, Platform, } from "react-native"; import { HMSSDK, HMSUpdateListenerActions, HMSConfig, HMSTrackType, HMSTrackUpdate, HMSPeerUpdate, HMSTrackSource, } from "@100mslive/react-native-hms"; import { requestCameraPermissionsAsync, requestMicrophonePermissionsAsync, } from "expo-camera"; /** * Take Room Code from Dashbaord for this sample app. * For more info, Check out {@link https://www.100ms.live/docs/react-native/v2/get-started/token#get-room-code-from-100ms-dashboard | Room Code} */ const ROOM_CODE = "rlk-lsml-aiy"; // OR, PASTE ROOM CODE FROM DASHBOARD HERE const USERNAME = "Test User"; const App = () => { const [joinRoom, setJoinRoom] = useState(false); const navigate = useCallback( (screen) => setJoinRoom(screen === "RoomScreen"), [], ); return ( <SafeAreaView style={{ flex: 1, backgroundColor: "#EFF7FF" }}> <StatusBar barStyle={"dark-content"} /> {joinRoom ? ( <RoomScreen navigate={navigate} /> ) : ( <HomeScreen navigate={navigate} /> )} </SafeAreaView> ); }; export default App; //#region Screens const HomeScreen = ({ navigate }) => { // Function to handle "Join Room" button press const handleJoinPress = async () => { const permissionResults = await Promise.allSettled([ requestCameraPermissionsAsync(), requestMicrophonePermissionsAsync(), ]); const permissionsGranted = permissionResults.every( (result) => result.status === "fulfilled", ); if (permissionsGranted) { navigate("RoomScreen"); } else { console.log("Permission Not Granted!"); } }; return ( <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}> <TouchableHighlight onPress={handleJoinPress} underlayColor="#143466" style={{ paddingHorizontal: 20, paddingVertical: 12, backgroundColor: "#2471ED", borderRadius: 8, }} > <Text style={{ fontSize: 20, color: "#ffffff" }}>Join Room</Text> </TouchableHighlight> </View> ); }; const RoomScreen = ({ navigate }) => { /** * `usePeerTrackNodes` hook takes care of setting up {@link HMSSDK | HMSSDK} instance, joining room and adding all required event listeners. * It gives us: * 1. peerTrackNodes - This is a list of {@link PeerTrackNode}, we can use this list to render local and remote peer tiles. * 2. loading - We can show loader while Room Room join is under process. * 3. leaveRoom - This is a function that can be called on a button press to leave room and go back to Welcome screen. */ const { peerTrackNodes, loading, leaveRoom, hmsInstanceRef } = usePeerTrackNodes({ navigate }); const HmsView = hmsInstanceRef.current?.HmsView; const _keyExtractor = (item) => item.id; // `_renderItem` function returns a Tile UI for each item which is `PeerTrackNode` object const _renderItem = ({ item }) => { const { peer, track } = item; return ( <View style={{ height: 300, margin: 8, borderRadius: 20, overflow: "hidden", backgroundColor: "#A0C3D2", }} > {/* Checking if we have "HmsView" component, valid trackId and "track is not muted" */} {HmsView && track && track.trackId && !track.isMute() ? ( // To Render Peer Live Videos, We can use HMSView // For more info about its props and usage, Check out {@link https://www.100ms.live/docs/react-native/v2/features/render-video | Render Video} <HmsView key={track.trackId} trackId={track.trackId} mirror={peer.isLocal} style={{ flex: 1 }} /> ) : ( <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }} > <View style={{ width: 100, height: 100, borderRadius: 50, alignItems: "center", justifyContent: "center", backgroundColor: "#FD8A8A", }} > <Text style={{ textAlign: "center", fontSize: 28, fontWeight: "bold", textTransform: "uppercase", }} > {peer.name .split(" ") .map((item) => item[0]) .join("")} </Text> </View> </View> )} </View> ); }; const handleRoomEnd = () => { leaveRoom(); navigate("HomeScreen"); }; return ( <View style={{ flex: 1 }}> {loading ? ( // Showing loader while Join is under process <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }} > <ActivityIndicator size={"large"} color="#2471ED" /> </View> ) : ( <View style={{ flex: 1, position: "relative" }}> {peerTrackNodes.length > 0 ? ( // Rendering list of Peers <FlatList centerContent={true} data={peerTrackNodes} showsVerticalScrollIndicator={false} keyExtractor={_keyExtractor} renderItem={_renderItem} contentContainerStyle={{ paddingBottom: 120, flexGrow: Platform.OS === "android" ? 1 : undefined, justifyContent: Platform.OS === "android" ? "center" : undefined, }} /> ) : ( <View style={{ flex: 1, alignItems: "center", justifyContent: "center", }} > <Text style={{ fontSize: 28, marginBottom: 32 }}>Welcome!</Text> <Text style={{ fontSize: 16 }}>You’re the first one here.</Text> <Text style={{ fontSize: 16 }}> Sit back and relax till the others join. </Text> </View> )} {/* Button to Leave Room */} <TouchableHighlight onPress={handleRoomEnd} underlayColor="#6e2028" style={{ position: "absolute", bottom: 40, alignSelf: "center", backgroundColor: "#CC525F", width: 60, height: 60, borderRadius: 30, alignItems: "center", justifyContent: "center", }} > <Text style={{ textAlign: "center", color: "#ffffff", fontWeight: "bold", }} > Leave Room </Text> </TouchableHighlight> </View> )} </View> ); }; //#endregion Screens /** * Sets up HMSSDK instance, Adds required Event Listeners * Checkout Quick Start guide to know things covered {@link https://www.100ms.live/docs/react-native/v2/guides/quickstart | Quick Start Guide} */ export const usePeerTrackNodes = ({ navigate }) => { const hmsInstanceRef = useRef(null); // We will save `hmsInstance` in this ref const [loading, setLoading] = useState(true); const [peerTrackNodes, setPeerTrackNodes] = useState([]); // Use this state to render Peer Tiles /** * Handles Room leave process */ const handleRoomLeave = async () => { try { const hmsInstance = hmsInstanceRef.current; if (!hmsInstance) { return Promise.reject("HMSSDK instance is null"); } // Removing all registered listeners hmsInstance.removeAllListeners(); /** * Leave Room. For more info, Check out {@link https://www.100ms.live/docs/react-native/v2/features/leave | Leave Room} */ const leaveResult = await hmsInstance.leave(); console.log("Leave Success: ", leaveResult); /** * Free/Release Resources. For more info, Check out {@link https://www.100ms.live/docs/react-native/v2/features/release-resources | Release Resources} */ const destroyResult = await hmsInstance.destroy(); console.log("Destroy Success: ", destroyResult); // Removing HMSSDK instance hmsInstanceRef.current = null; } catch (error) { console.log("Leave or Destroy Error: ", error); } }; /** * Handles Join Update received from {@link HMSUpdateListenerActions.ON_JOIN} event listener * Receiving This event means User (that is Local Peer) has successfully joined room * @param {Object} data - object which has room object * @param {Object} data.room - current {@link HMSRoom | room} object */ const onJoinSuccess = (data) => { /** * Checkout {@link HMSLocalPeer | HMSLocalPeer} Class */ const { localPeer } = data.room; // Creating or Updating Local Peer Tile // `updateNode` function updates "Track and Peer objects" in PeerTrackNodes and returns updated list. // if none exist then we are "creating a new PeerTrackNode with the received Track and Peer" setPeerTrackNodes((prevPeerTrackNodes) => updateNode({ nodes: prevPeerTrackNodes, peer: localPeer, track: localPeer.videoTrack, createNew: true, }), ); // Turning off loading state on successful Room Room join setLoading(false); }; /** * Handles Peer Updates received from {@link HMSUpdateListenerActions.ON_PEER_UPDATE} event listener * @param {Object} data - This has updated peer and update type * @param {HMSPeer} data.peer - Updated Peer * @param {HMSPeerUpdate} data.type - Update Type */ const onPeerListener = ({ peer, type }) => { // We will create Tile for the Joined Peer when we receive `HMSUpdateListenerActions.ON_TRACK_UPDATE` event. // Note: We are chosing to not create Tiles for Peers which does not have any tracks if (type === HMSPeerUpdate.PEER_JOINED) return; if (type === HMSPeerUpdate.PEER_LEFT) { // Remove all Tiles which has peer same as the peer which just left the room. // `removeNodeWithPeerId` function removes peerTrackNodes which has given peerID and returns updated list. setPeerTrackNodes((prevPeerTrackNodes) => removeNodeWithPeerId(prevPeerTrackNodes, peer.peerID), ); return; } if (peer.isLocal) { // Updating the LocalPeer Tile. // `updateNodeWithPeer` function updates Peer object in PeerTrackNodes and returns updated list. // if none exist then we are "creating a new PeerTrackNode for the updated Peer". setPeerTrackNodes((prevPeerTrackNodes) => updateNodeWithPeer({ nodes: prevPeerTrackNodes, peer, createNew: true, }), ); return; } if ( type === HMSPeerUpdate.ROLE_CHANGED || type === HMSPeerUpdate.METADATA_CHANGED || type === HMSPeerUpdate.NAME_CHANGED || type === HMSPeerUpdate.NETWORK_QUALITY_UPDATED ) { // Ignoring these update types because we want to keep this implementation simple. return; } }; /** * Handles Track Updates received from {@link HMSUpdateListenerActions.ON_TRACK_UPDATE} event listener * @param {Object} data - This has updated track with peer and update type * @param {HMSPeer} data.peer - Peer * @param {HMSTrack} data.track - Peer Track * @param {HMSTrackUpdate} data.type - Update Type */ const onTrackListener = ({ peer, track, type }) => { // on TRACK_ADDED update // We will update Tile with the track or // create new Tile for with the track and peer if ( type === HMSTrackUpdate.TRACK_ADDED && track.type === HMSTrackType.VIDEO ) { // We will only update or create Tile "with updated track" when track type is Video. // Tiles without Video Track are already respresenting Peers with or without Audio. // Updating the Tiles with Track and Peer. // `updateNode` function updates "Track and Peer objects" in PeerTrackNodes and returns updated list. // if none exist then we are "creating a new PeerTrackNode with the received Track and Peer". setPeerTrackNodes((prevPeerTrackNodes) => updateNode({ nodes: prevPeerTrackNodes, peer, track, createNew: true, }), ); return; } // on TRACK_MUTED or TRACK_UNMUTED updates, We will update Tiles (PeerTrackNodes) if ( type === HMSTrackUpdate.TRACK_MUTED || type === HMSTrackUpdate.TRACK_UNMUTED ) { // We will only update Tile "with updated track" when track type is Video. if (track.type === HMSTrackType.VIDEO) { // Updating the Tiles with Track and Peer. // `updateNode` function updates "Track and Peer objects" in PeerTrackNodes and returns updated list. // Note: We are not creating new PeerTrackNode object. setPeerTrackNodes((prevPeerTrackNodes) => updateNode({ nodes: prevPeerTrackNodes, peer, track, }), ); } else { // Updating the Tiles with Peer. // `updateNodeWithPeer` function updates Peer object in PeerTrackNodes and returns updated list. // Note: We are not creating new PeerTrackNode object. setPeerTrackNodes((prevPeerTrackNodes) => updateNodeWithPeer({ nodes: prevPeerTrackNodes, peer, }), ); } return; } if (type === HMSTrackUpdate.TRACK_REMOVED) { // If non-regular track, or // both regular video and audio tracks are removed // Then we will remove Tiles (PeerTrackNodes) with removed track and received peer return; } /** * For more info about Degrade/Restore. check out {@link https://www.100ms.live/docs/react-native/v2/features/auto-video-degrade | Auto Video Degrade} */ if ( type === HMSTrackUpdate.TRACK_RESTORED || type === HMSTrackUpdate.TRACK_DEGRADED ) { return; } }; /** * Handles Errors received from {@link HMSUpdateListenerActions.ON_ERROR} event listener * @param {HMSException} error * * For more info, Check out {@link https://www.100ms.live/docs/react-native/v2/features/error-handling | Error Handling} */ const onErrorListener = (error) => { setLoading(false); console.log(`${error?.code} ${error?.description}`); }; // Effect to handle HMSSDK initialization and Listeners Setup useEffect(() => { const joinRoom = async () => { try { setLoading(true); /** * creating {@link HMSSDK} instance to join room * For more info, Check out {@link https://www.100ms.live/docs/react-native/v2/features/join#join-a-room | Join a Room} */ const hmsInstance = await HMSSDK.build(); // Saving `hmsInstance` in ref hmsInstanceRef.current = hmsInstance; const token = await hmsInstance.getAuthTokenByRoomCode(ROOM_CODE); /** * Adding HMSSDK Event Listeners before calling Join method on HMSSDK instance * For more info, Check out - * {@link https://www.100ms.live/docs/react-native/v2/features/join#update-listener | Adding Event Listeners before Join}, * {@link https://www.100ms.live/docs/react-native/v2/features/event-listeners | Event Listeners}, * {@link https://www.100ms.live/docs/react-native/v2/features/event-listeners-enums | Event Listeners Enums} */ hmsInstance.addEventListener( HMSUpdateListenerActions.ON_JOIN, onJoinSuccess, ); hmsInstance.addEventListener( HMSUpdateListenerActions.ON_PEER_UPDATE, onPeerListener, ); hmsInstance.addEventListener( HMSUpdateListenerActions.ON_TRACK_UPDATE, onTrackListener, ); hmsInstance.addEventListener( HMSUpdateListenerActions.ON_ERROR, onErrorListener, ); /** * Joining Room. For more info, Check out {@link https://www.100ms.live/docs/react-native/v2/features/join#join-a-room | Join a Room} */ hmsInstance.join( new HMSConfig({ authToken: token, username: USERNAME }), ); } catch (error) { navigate("HomeScreen"); console.error(error); Alert.alert("Error", "Check your console to see error logs!"); } }; joinRoom(); // When effect unmounts for any reason, We are calling leave function return () => { handleRoomLeave(); }; }, [navigate]); return { loading, leaveRoom: handleRoomLeave, peerTrackNodes, hmsInstanceRef, }; }; //#region Utilities /** * returns `uniqueId` for a given `peer` and `track` combination */ export const getPeerTrackNodeId = (peer, track) => { return peer.peerID + (track?.source ?? HMSTrackSource.REGULAR); }; /** * creates `PeerTrackNode` object for given `peer` and `track` combination */ export const createPeerTrackNode = (peer, track) => { let isVideoTrack = false; if (track && track?.type === HMSTrackType.VIDEO) { isVideoTrack = true; } const videoTrack = isVideoTrack ? track : undefined; return { id: getPeerTrackNodeId(peer, track), peer: peer, track: videoTrack, }; }; /** * Removes all nodes which has `peer` with `id` same as the given `peerID`. */ export const removeNodeWithPeerId = (nodes, peerID) => { return nodes.filter((node) => node.peer.peerID !== peerID); }; /** * Updates `peer` of `PeerTrackNode` objects which has `peer` with `peerID` same as the given `peerID`. * * If `createNew` is passed as `true` and no `PeerTrackNode` exists with `id` same as `uniqueId` generated from given `peer` and `track` * then new `PeerTrackNode` object will be created. */ export const updateNodeWithPeer = (data) => { const { nodes, peer, createNew = false } = data; const peerExists = nodes.some((node) => node.peer.peerID === peer.peerID); if (peerExists) { return nodes.map((node) => { if (node.peer.peerID === peer.peerID) { return { ...node, peer }; } return node; }); } if (!createNew) return nodes; if (peer.isLocal) { return [createPeerTrackNode(peer), ...nodes]; } return [...nodes, createPeerTrackNode(peer)]; }; /** * Removes all nodes which has `id` same as `uniqueId` generated from given `peer` and `track`. */ export const removeNode = (nodes, peer, track) => { const uniqueId = getPeerTrackNodeId(peer, track); return nodes.filter((node) => node.id !== uniqueId); }; /** * Updates `track` and `peer` of `PeerTrackNode` objects which has `id` same as `uniqueId` generated from given `peer` and `track`. * * If `createNew` is passed as `true` and no `PeerTrackNode` exists with `id` same as `uniqueId` generated from given `peer` and `track` * then new `PeerTrackNode` object will be created */ export const updateNode = (data) => { const { nodes, peer, track, createNew = false } = data; const uniqueId = getPeerTrackNodeId(peer, track); const nodeExists = nodes.some((node) => node.id === uniqueId); if (nodeExists) { return nodes.map((node) => { if (node.id === uniqueId) { return { ...node, peer, track }; } return node; }); } if (!createNew) return nodes; if (peer.isLocal) { return [createPeerTrackNode(peer, track), ...nodes]; } return [...nodes, createPeerTrackNode(peer, track)]; }; //#endregion Utility

We encourage you to explore the relevant documentation and guides for further details. If you have any questions or need assistance, feel free to ask in the support email support@100ms.live

Test run your app

Now let's apply changes to native iOS and Android folders by running below command in terminal -

npx expo prebuild

Chose target platform and run command to test run app -

npx expo run:android

The above commands compile your project, using our locally installed Android SDK, into a debug build of your app. Learn more about running android app locally here

Next Steps

We have multiple example apps to get you started with 100ms React Native SDK -

Basic Example

For a Basic Sample Reference, check out the React Native Quickstart Sample App on GitHub.

You can also check out the fully featured Example App implementation in the 100ms React Native SDK GitHub repository showcasing multiple features provided by 100ms.

Check Deployed Sample Apps

You can download and check out the 100ms React Native deployed apps -

🤖 Download the Sample Android App here

📲 Download the Sample iOS App here


Have a suggestion? Recommend changes ->

Was this helpful?

1234