Relay SDK for React Native
Getting Started
The Relay React Native SDK allows you to integrate Relay into React Native applications, enabling developers to directly make audio and video calls to phone numbers, SIP endpoints, browsers and other Apps. Using the React Native SDK you can add immersive, scalable communication - from video conferences and softphones to mobile gaming - all available right in your own App.
SignalWire's simple and powerful authentication system, using JWT, allows you to set granular permissions, enabling some of your users to only join conference calls, while others could list on-going calls and jump in to assist from a support dashboard... the possibilities are endless.
Source Code: signalwire/signalwire-react-native
Support: SignalWire Community Slack Channel
Installation
The Relay SDK for React Native is easy to use and only takes a few minute to setup and get running.
npm install @signalwire/react-native @react-native-async-storage/async-storage
During the installation process the SDK will try to automatically install and link these React Native libraries for you:
- react-native-webrtc: The engine required to enable the WebRTC APIs on your App.
- react-native-incall-manager: Native library to manage sensors and events during a call.
- async-storage: Key/Value storage system for React Native.
iOS Setup
Make sure to add the permissions into the Info.plist
file to access microphone and webcam:
<key>NSCameraUsageDescription</key>
<string>Enter here the message to display to the user to ask access to the Camera</string>
<key>NSMicrophoneUsageDescription</key>
<string>Enter here the message to display to the user to ask access to the Microphone</string>
To troubleshoot any build errors, follow the steps for each native library:
- Installation instruction for react-native-webrtc
- Installation instruction for react-native-incall-manager
- Installation instruction for async-storage
Android Setup
Make sure to add the permissions in android/app/src/main/AndroidManifest.xml
:
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
To troubleshoot any build errors, follow the steps for each native library:
- Installation instruction for react-native-webrtc
- Installation instruction for react-native-incall-manager
- Installation instruction for async-storage
Using the SDK
First step to using the SDK is to setup authentication.
To avoid packaging and releasing the private Project Token into your own App, the React Native SDK uses JSON Web Tokens (JWT) to authenticate with SignalWire and apply fine grained permissions to the end-user.
Your server uses your Project ID and Project Token to make a request to generate a JWT with your specific requirements, such as expiration time, permissions, resource name, and give the resulting JWT Token to the App. The JWT is safe to expose to the end user, it is signed and cannot be edited. The App can then log into SignalWire using the Project ID and the JWT.
To learn more about generating and using JWT, including all the options available to you, visit Authentication for React Native SDK Documentation.
Authentication using JWT
The SDKs that run on the client side, like the JS SDK or React Native SDK, cannot safely use the Project Token to authenticate your users as you do in the other, server-side SDKs.
SignalWire uses JSON Web Tokens (JWT), an open-standard, to authorize browsers and mobile applications without exposing your secure Project Token and Keys in client-side applications.
How Does It Work?
You start by creating a token on your server and specify what capabilities and permissions you'd like your endpoint to have. You can then connect to Relay within the SDKs using your Project ID
and JWT
.
Think of it as if you are generating a long, temporary password for each endpoint you want to connect. There is no limit to the number of JWTs you can generate.
Security
Security is one of the basic principles of SignalWire and Relay, and we use JSON Web Tokens for client-side authorization because they are an open, industry standard method of securely representing authorization between two parties.
Relay JWT allows you to specify find-grained permissions, or scopes, to determine what access rights are granted, as well as expiration and identification. These settings are determined by you and signed by SignalWire when the JWT is created and cannot be altered or tampered with on the client-side.
Expiration
All Relay JWT have an expiration time, to protect from abuse. When a token's expiration is up, the client will be disconnected from Relay automatically.
By default, all Relay JWT have an expiration time of 15 minutes, but you should create tokens with the shortest possible expiration that makes sense for your application.
Relay JWT can also easily be refreshed, updating an existing token with a new expiration time. This allows you to create tokens with short expirations that can be frequently extended as required by your application.
Resource
When a client connects using the JavaScript SDK, they are creating an endpoint, in which (assuming they have been granted permission) they can send and receive calls to. This is referred to as the resource
.
When generating a token, you can specify the resource name of the client. For example, if a user logs into your application with the username alice
, you might want to generate tokens for them with the resource name set to alice
. Now, another application can simply dial "alice", to reach her, or calls made by Alice's app would be seen as coming from "alice".
If a resource is not set when generating a JWT, a random UUID will be used.
Creating Tokens
To create a new JWT you send a POST
request to the JWT REST endpoint. The response will contain a JWT and Refresh Token, which are valid immediately.
Note: The JWT is safe to expose to the client, but the refresh_token
should be kept secret.
Parameter | |
---|---|
resource optional |
The endpoint's resource name. Defaults to a random UUID. |
expires_in optional |
The number of minutes this token will expire in. Defaults to 15 minutes. |
POST /api/relay/rest/jwt
curl https://your-space.signalwire.com/api/relay/rest/jwt \
-X POST \
-u 'YourProjectID:YourAuthToken'
Response
201 CREATED
{
"jwt_token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleGFtcGxlIjoiYW4gZXhhbXBsZSBKV1QiLCJpYXQiOjE1NTk3NTk4MDQsImlzcyI6IlNpZ25hbFdpcmUgSldUIiwicmVzb3VyY2UiOiI1NWY1OThlOC1mNzdiLTQzMzktYTA0MC01YTMwNWJiMmRhYTUiLCJleHAiOjE1NTk3NjA3MDR9.8ReiwXsi8aIaQM4AyUErIe1WF8bTaFNO5e5h3_jxgUd4AqQpwHoUdl7nQJWskClEehBEXzEz8st5TQfOpWD8xg",
"refresh_token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IlJlZnJlc2gifQ.eyJleGFtcGxlIjoiQW4gRXhhbXBsZSBSZWZyZXNoIFRva2VuIiwiaWF0IjoxNTU5NzU5ODA0LCJpc3MiOiJTaWduYWxXaXJlIEpXVCJ9.WP8af16vR8LlM5rZ8kFpILehcMQpP6TswW9VNtQf9eVPGmnQjUiHpbYWwevo9CRHhMpNLi3Mi3a3DsCl4XN-vQ"
}
Refreshing Tokens
To extend an existing JWT, send a PUT
request with the JWT's Refresh Token to the JWT REST endpoint. The response will contain a new JWT and Refresh Token, which are valid immediately.
Parameter | |
---|---|
refresh_token required |
A valid refresh token. |
PUT /api/relay/rest/jwt
curl https://your-space.signalwire.com/api/relay/rest/jwt \
-X PUT \
-u 'YourProjectID:YourAuthToken' \
-H 'Content-Type: application/json' \
-d '{ "refresh_token": "a_valid_refresh_token" }'
Response
200 OK
{
"jwt_token": "a_new_jwt_token",
"refresh_token": "a_new_jwt_refresh_token"
}
Generate a JWT
To generate a JWT, make a server-side POST
request to the JWT endpoint on the Relay REST API.
curl https://your-space.signalwire.com/api/relay/rest/jwt \
-X POST \
-u 'YourProjectID:YourProjectToken' \
-H 'Content-Type: application/json'
Will result in a JSON response like:
{
"jwt_token": "a_long_jwt_token",
"refresh_token": "a_long_jwt_refresh_token"
}
For more information and examples on generating JSON Web Tokens, visit Authentication for React Native SDK Documentation
Connect using JWT
Using the JWT you received in the previous step, you can connect to Relay using your Project ID and the JWT.
const client = new Relay({
project: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
token: "a_long_jwt_token",
});
You can then use client
to make Relay requests.
Refresh JWT Token
All tokens have an expiration, so that a user cannot stay logged in forever. You can use the refresh token you received in Generate a JWT to refresh a token you already generated.
To refresh a JWT, make a server-side PUT
request to the JWT endpoint with the refresh token:
curl https://your-space.signalwire.com/api/relay/rest/jwt \
-X PUT \
-u 'YourProjectID:YourProjectToken' \
-H 'Content-Type: application/json' \
-d '{
"refresh_token": "a_long_jwt_token"
}'
Will result in a JSON response like:
{
"jwt_token": "a_new_jwt_token",
"refresh_token": "a_new_jwt_refresh_token"
}
For more information about automatically refreshing JWT as they're about to expire, see refreshToken
Event Documentation
API Reference
Relay
Relay
client is the basic connection to Relay, allowing you send commands to Relay and setup handlers for inbound events.
Constructor
Constructs a client object to interact with Relay.
Parameters
project |
string | required | Project ID from your SignalWire Space |
token |
string | required | Json Web Token retrieved using Rest API. See Generate a JWT for more information. |
Examples
Create a Client to interact with the Relay API.
const client = new Relay({
project: 'my-project-id',
token: 'my-jwt',
})
client.on('signalwire.ready', (client) => {
// You are connected with Relay!
})
client.connect()
Properties
connected |
boolean | true if the client has connected to Relay. |
expired |
boolean | true if the JWT has expired. |
Devices and Media Constraints
You can configure the devices your client
will use by default with these properties and methods:
devices getter |
object | All devices recognized by the client keyed by kind: videoinput , audioinput and audiooutput . |
videoDevices getter |
object | Video devices recognized by the client. |
audioInDevices getter |
object | Audio input devices recognized by the client. |
audioOutDevices getter |
object | Audio output devices recognized by the client. |
mediaConstraints getter |
object | Current audio/video constraints used by the client. |
speaker getter |
string | Audio output device used by the client. |
speaker setter |
string | Set the audio output device to use for the subsequent calls. |
PRO TIP:
videoDevices
,audioInDevices
,audioOutDevices
objects havedeviceId
as a key.
If you want an array instead, use.toArray()
method on them. See the example below.
Examples
Retrieve video devices as object and then as array.
const asObject = client.videoDevices
// {
// "0a294ce2885d06ffb17e86e184e357bc970730d4116526c6e2": {
// "deviceId": "0a294ce2885d06ffb17e86e184e357bc970730d4116526c6e2",
// "kind": "videoinput",
// "label": "GENERAL WEBCAM",
// "groupId": "e425504b346c1c9d7fbda3c035862ea4373420bfa1d23210d6"
// }
// }
const asArray = client.videoDevices.toArray()
// [
// {
// "deviceId": "0a294ce2885d06ffb17e86e184e357bc970730d4116526c6e2",
// "kind": "videoinput",
// "label": "GENERAL WEBCAM",
// "groupId": "e425504b346c1c9d7fbda3c035862ea4373420bfa1d23210d6"
// }
// ]
If present, use the first audio output device as default speaker.
const speakerList = client.audioOutDevices.toArray()
if (speakerList.length) {
client.speaker = speakerList[0].deviceId
}
STUN/TURN Servers
Through the iceServers
you can set/retrieve the default ICE server configuration for all subsequent calls.
iceServers getter |
RTCIceServers | Current ICE servers used by the client. |
iceServers setter |
RTCIceServers[] or Boolean | array of ICE servers, true to use the default ones or false to not use STUN/TURN at all. |
Methods
checkPermissions
The first time your App will try to access microphone or webcam, the OS will display a notification to the user. Use this method if you want to request the permissions to access them.
Available In:
Parameters
audio |
boolean | optional | Whether to check permissions for the microphone. Default to true |
video |
boolean | optional | Whether to check permissions for the webcam. Default to true |
Returns
Promise<boolean>
- A Promise object resolved with a boolean value.
Examples
Check both audio and video permissions.
// within an async function ..
const success = await client.checkPermissions()
if (success) {
// The user has given permission..
} else {
// The user has not given permission!
}
connect
Activates the connection to Relay. Make sure you have attached the listeners you need before connecting the client, or you might miss some events.
Available In:
Returns
Promise<void>
Examples
await client.connect()
disconnect
Disconnect the client from Relay.
Available In:
Returns
void
Examples
client.disconnect()
disableMicrophone
Disable the use of the microphone
for the subsequent calls.
Available In:
disableWebcam
Disable the use of the webcam
for the subsequent calls.
Available In:
enableMicrophone
Enable the use of the microphone
for the subsequent calls.
Available In:
enableWebcam
Enable the use of the webcam
for the subsequent calls.
Available In:
newCall
Make a new outbound call.
Parameters
options |
object | required | Object with the following properties: |
destinationNumber |
string | required | Extension to dial. |
id |
string | optional | The identifier of the Call. |
localStream |
string | optional | If set, the Call will use this stream instead of retrieving a new one. |
iceServers |
RTCIceServers[] | optional | Overrides client's default iceServers . |
audio |
boolean | optional | Overrides client's default audio constraints. |
video |
boolean | optional | Overrides client's default video constraints. |
useStereo |
boolean | optional | Use stereo audio instead of mono. |
onNotification |
string | optional | Overrides client's default signalwire.notification handler for this Call. |
Returns
Promise<Call>
- A Promise fulfilled with the new outbound Call object or rejected with the error.
Examples
Make an outbound call to the conference
35123
using default values from the Client.
// within an async function ..
const options = { destinationNumber: '35123' }
const call = await client.newCall(options).catch(console.error)
on
Attach an event handler for a specific type of event.
Available In:
Parameters
event |
string | required | Event name. Full list of events Relay Events |
handler |
function | required | Function to call when the event comes. |
Returns
Relay
- The client object itself.
Examples
Subscribe to the
signalwire.ready
andsignalwire.error
events.
client.on('signalwire.ready', (client) => {
// Your client is ready!
}).on('signalwire.error', (error) => {
// Got an error...
})
off
Remove an event handler that were attached with .on()
. If no handler
parameter is passed, all listeners for that event
will be removed.
Parameters
event |
string | required | Event name. Full list of events Relay Events |
handler |
function | optional | Function to remove. Note: handler will be removed from the stack by reference so make sure to use the same reference in both .on() and .off() methods. |
Returns
Relay
- The client object itself.
Examples
Subscribe to the
signalwire.error
and then, remove the event handler.
const errorHandler = (error) => {
// Log the error..
}
client.on('signalwire.error', errorHandler)
// .. later
client.off('signalwire.error', errorHandler)
refreshToken
When the JWT is going to expire, the Client dispatch a notification with type refreshToken
that allows you to refresh the token and keep your session alive.
Available In:
Parameters
token |
string | required | New JWT to keep your session alive. |
Returns
Promise<void>
Examples
Listen for all notifications and, on
refreshToken
, fetch a new JWT from your backend and update the token on the client.
client.on('signalwire.notification', function(notification) {
switch (notification.type) {
case 'refreshToken':
// Take a new token from your server...
xhrRequestToRefreshYourJWT().then(async (newToken) => {
await client.refreshToken(newToken).catch(console.error)
})
break
}
})
Events
All available events you can attach a listener on.
signalwire.ready |
The session has been established and all other methods can now be used. |
signalwire.error |
There is an error dispatch at the session level. |
signalwire.notification |
A notification from SignalWire. Notifications can refer to calls or session updates. |
signalwire.socket.open |
The websocket is open. However, you have not yet been authenticated. |
signalwire.socket.error |
The websocket gave an error. |
signalwire.socket.message |
The client has received a message from the websocket. |
signalwire.socket.close |
The websocket is closing. |
Call
A Call represents a one-to-one call with another browser, a SIP endpoint, or even a phone number. The Call object supports both audio and video.
Properties
id |
string | The identifier of the call. |
direction |
string | The direction of the call. Can be either inbound or outbound . |
state |
string | The state of the call. See below for all the possible call states. |
prevState |
string | The previous state of the call. See below for all the possible call states. |
localStream |
MediaStream | The local stream of the call. This can be used in a video/audio element to play the local media. |
remoteStream |
MediaStream | The remote stream of the call. This can be used in a video/audio element to play the remote media. |
State
The state
and prevState
properties of a Call have the following values:
Value | Description |
---|---|
new |
New Call has been created in the client. |
trying |
You are attempting to call someone. |
requesting |
Your outbound call is being sent to the server. |
recovering |
Your previous call is recovering after the page refresh. If you refresh the page during a call, you will automatically be joined with the latest call. |
ringing |
Someone is attempting to call you. |
answering |
You are attempting to answer the inbound Call. |
early |
You received the media before the Call has been answered. |
active |
Call has become active. |
held |
Call has been held. |
hangup |
Call has ended. |
destroy |
Call has been destroyed. |
purge |
Call has been purged. |
Methods
answer
Start the process to answer the incoming Call.
Available In:
Parameters
None
Returns
None
Example
call.answer()
deaf
Turn off the audio input track.
Available In:
Example
call.deaf()
dtmf
Send a Dual Tone Multi Frequency (DTMF) string to Relay.
Available In:
Parameters
string |
string | required | DTMF to send. |
Returns
None
Examples
call.dtmf('0')
hangup
Hangs up the call.
Available In:
Parameters
None
Returns
None
Examples
call.hangup()
hold
Holds the call.
Available In:
Parameters
None
Returns
None
Examples
call.hold()
muteAudio
Turn off the audio output track.
Available In:
Example
call.muteAudio()
muteVideo
Turn off the video output track.
Available In:
Example
call.muteAudio()
setSpeakerPhone
By default the library route the audio to earpiece if the call has not a video track, otherwise route to the speakerphone.
Using setSpeakerPhone()
you can change the behaviour while in call.
Available In:
Parameters
flag |
boolean | required | true/false to route audio to speakerphone or not. |
Returns
None
Example
call.setSpeakerPhone(true)
switchCamera
Switch the camera currently used by the Call.
Note: in recent devices with more then 1 rear camera the switch method will try to use all the available devices.
Available In:
Example
call.switchCamera()
toggleAudioMute
Toggle the audio output track.
Available In:
Example
call.toggleAudioMute()
toggleDeaf
Toggle the audio input track.
Available In:
Example
call.toggleDeaf()
toggleHold
Toggles the hold state of the call.
Available In:
Parameters
None
Returns
None
Examples
call.toggleHold()
toggleVideoMute
Toggle the video output track.
Available In:
Example
call.toggleVideoMute()
undeaf
Turn on the audio input track.
Available In:
Example
call.undeaf()
unhold
Un-holds the call.
Available In:
Parameters
None
Returns
None
Examples
call.unhold()
unmuteAudio
Turn on the audio output track.
Available In:
Example
call.unmuteAudio()
unmuteVideo
Turn on the video output track.
Available In:
Example
call.unmuteVideo()
Notification
A notification is an event that SignalWire dispatches to notify the Client about different cases. A notification can refer to the JWT expiration, Call changes or Conference updates.
Types
Every notification has a property type
that identify the case and the structure of the data.
The available type are:
Value | Description |
---|---|
refreshToken |
The JWT is going to expire. Refresh it or your session will be disconnected. |
callUpdate |
A Call's state has been changed. Update the UI accordingly. |
participantData |
New participant’s data (i.e. name, number) to update the UI. |
userMediaError |
The user has not given the permission to access media devices. Check the audio and video constraints you are using. |
refreshToken
Your JWT is going to expire. Refresh it or your session will be disconnected.
Anatomy of a
refreshToken
notification.
{
type: 'refreshToken',
session: RelayInstance
}
callUpdate
A Call's state has been changed. It is useful to update the UI of your application.s
Anatomy of a
callUpdate
notification.
{
type: 'callUpdate',
call: CallObject
}
participantData
This notification contains the participant data for the current Call. This is useful when updating the UI.
Anatomy of a
participantData
notification.
{
type: 'participantData',
call: CallObject,
displayName: 'David Roe',
displayNumber: '1777888800'
displayDirection: 'inbound'
}
userMediaError
The user has not given the permission to access media devices. You can ask the user to set the proper permission for your App in the OS settings.
Anatomy of a
userMediaError
notification.
{
type: 'userMediaError',
error: error
}
Examples
Checkout our examples in Github to help you get started quickly.
Visit https://github.com/signalwire/signalwire-node/tree/master/packages/react-native/examples for our latest list of example implementations using the React Native SDK.