Navbar

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.

Latest Version:

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:

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:

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:

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 have deviceId 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 and signalwire.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.