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.
Create a Sample app
This section contains instructions to create Expo App.
Prerequisites
- A 100ms account if you don't have one already.
- Familiarity with Basic concepts of 100ms like Templates, Rooms, Peers, Roles, Tracks, etc.
- Working Expo Local App Development Environment as we need to make changes in android and ios folders.
- Familiarity with basics of Expo.
- VS code or any other IDE.
Create a Expo app
Once you have the prerequisites, follow the steps below to create a Expo app.
-
Open your Terminal and navigate to directory/folder where you would like to create your app.
-
Run the below command to create Expo app:
npx create-expo-app my-expo-app && cd my-expo-app
- 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
- 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.
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.
- 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.
- Add below
expo-camera
plugin config in your expo plugin file that is root levelapp.json
file
{ "expo": { "plugins": [ [ "expo-camera", { "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera", "microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone", "recordAudioAndroid": true } ] ] } }
- Android platform requires some additional permissions. Add below permissions inside
android
inapp.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"]} ... } }
- 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 asHMSPeerUpdate.PEER_JOINED
,HMSPeerUpdate.PEER_LEFT
, orHMSPeerUpdate.NAME_CHANGED
, etc. Refer to the HMSPeerUpdate enum to learn about all available update types in theON_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 asHMSTrackUpdate.TRACK_ADDED
,HMSTrackUpdate.TRACK_REMOVED
, orHMSTrackUpdate.TRACK_MUTED
, etc. Refer to the HMSTrackUpdate enum to learn about all available update types in theON_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 -
- 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
- 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 theJoin 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 inApp.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.
- 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 100ms community!
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.
Full-featured Example
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