Deploying WebRTC on an Expo React Native app

If you are a React Native developer, chances are that you have heard of Expo. In fact, I’d go as far as declaring you have definitely heard of Expo. As the environment that dramatically simplified building, testing, and publishing native applications, Expo CLI has eclipsed React Native CLI as the default environment for building native applications.

Expo takes a very opinionated approach to framework design; it specifically prioritizes ease of testing, code consolidation, and standardization. And Expo succeeds in all of those; building a simple mobile app, such as a note-taking app, is and was always a breeze in Expo. But when Expo first started, it was hindered by compatibility issues. For the longest time, whenever a developer needed a native package or third-party plugin… problems arose.

Expo was fairly incompatible with packages built outside of the Expo’s core ecosystem. Technically, developers could include packages if they were willing to “eject” their Expo app (a fancy export feature); however, that process practically sabotaged the original benefits of Expo as an ejected app had to be compiled via Xcode/Android Studio with no access to Expo’s testing toolkit. One of the casualties of Expo’s opinionated design was WebRTC. WebRTC, the primary framework for real-time web-based video and audio communication, requires native toolkits to leverage camera and microphone devices. Because Expo couldn’t support native toolkits without ejecting, it wasn’t well suited for WebRTC apps.

Thankfully, today, Expo’s eject-riddled days are a relic of the past. Expo has dramatically evolved and improved. With Expo development builds and Expo config plugins, so much more is possible. Particularly, WebRTC and Expo can finally play ball. Today, we’ll uncover exactly why 2023 Expo is a great environment for building a WebRTC product.

Why Expo?

Let’s explore why Expo is a fantastic environment for building a React Native app. In particular, let’s uncover how it expedites development.

What is Expo?

Expo is a React Native environment that makes it easy to build, test, and deploy applications for all devices, spanning from iOS to Android. Expo is not a React Native competitor; instead, it is an opinionated React Native environment and toolkit. Expo takes over the code organization process, the build workflow, and the publishing process. Expo consolidates code for iOS and Android development better than stock React Native is capable of. And, one of the biggest perks is that it doesn’t require Xcode or Android Studio to work (unlike the stock React Native). A caveat, however, is sometimes you need to leverage EAS—Expo Application Service—which builds the project on the cloud, whenever native code is used.

What is Expo Go?

One of Expo’s signature features is a pre-built iOS and Android app named Expo Go that is available in both app stores. With Expo Go, developers can deploy updates to their applications via a wireless local network connection; Expo handles loading and booting the newly built JavaScript app bundle and leverages the JavaScript Hermes engine to execute it.

Expo Go is one of the core tenets behind Expo’s success; with Expo Go, it is significantly easier to push updates to an application. Expo Go is similar to how web app developers build applications using the webpack module bundler. However, since Expo Go is a base testing application distributed on respective app stores—not personalized for each individual repository’s needs— there are some real limitations.

Limitations of Expo Go

Expo Go cannot access native libraries or third-party plugins as they weren’t included in the original Expo Go source code. More specifically, Expo Go simply loads and boots the JavaScript bundle, which then uses a message-passing-like technique to access third-party plugins and native libraries. Since Expo Go doesn’t have those packages included in its bundle, it cannot execute the native code.

Expo has introduced Expo development builds and Expo config plugins to address this issue.

What are Expo development builds?

To address this limitation, Expo launched Expo development builds. Expo development builds allow developers to extend Expo Go’s codebase to ship their own personalized Expo Go application. With development builds, developers can create an Expo Go-like testing app with their required plugins bundled in. The only missing feature is that development builds don’t have Expo Go’s out-of-the-box distribution; while Expo Go was easy to download off app stores, Expo development builds need to be manually distributed.

However, since distributing development builds isn’t too different from distributing proprietary development toolkits, it is a savvy solution to a problem that had plagued Expo for years.

What are Expo config plugins?

Expo config plugins enable developers to extend the app config and customize the prebuild process for their apps. They allow third parties to create plugins form-fitted to Expo. Many config plugins are wrappers around native code. With config plugins, developers customize the prebuild process, which enables native modules to be included in the app binary.

Expo config plugins and Expo development builds work together to enable the entire Expo workflow work for native and third-party code. Because of them, WebRTC + Expo is made possible.


What is WebRTC?

WebRTC is an open-source project designed to power real-time audio and video chat applications; it has also been leveraged to power live streaming, online conferences, and live chat. Given that many mobile applications include video and audio call features—from collaboration platforms to dating apps—WebRTC support is important. Moreover, because many mobile applications need interoperability with web-app counterparts , WebRTC’s platform-agnostic, network-agnostic design makes it a very suitable candidate to power audio/video/chat functionality.

WebRTC essentially describes how to exchange media capabilities and then send the negotiated media between two endpoints. However, it does require a separate signaling server to negotiate and initiate a connection between two WebRTC endpoints.

Expectedly, WebRTC needs access to native components such as the camera and microphone to operate. Previously, building a WebRTC-compatible mobile application ruled out Expo due to its constraints in accessing native code; today, thankfully, Expo is a fantastic choice given Expo development builds and Expo config plugins.


How to implement WebRTC in Expo

Using a WebRTC Config Plugin

One of the most popular open-source WebRTC plugins for React Native is react-native-webrtc. Now compatible with Expo Go, react-native-webrtc is a decent option for developers looking to implement WebRTC in their React Native application. However, the plugin doesn’t provide a backend infrastructure to operate WebRTC applications, just client device code to partake in a WebRTC exchange.

There is an Expo-specific installation of react-native-webrtc available on npm. You can install the plugin by executing the following command in the CLI:

npx expo install react-native-webrtc @config-plugins/react-native-webrtc

After installation, the config plugin needs to be added to the plugins array of the Expo configuration file. This tells Expo to include the plugin in the prebuild process.

{
  "expo": {
    "plugins": ["@config-plugins/react-native-webrtc"]
		// ... 
  }
}

It’s worth mentioning that this often ships an error in Android, which Expo has closed, but still happens. If you face this issue, one possible solution is to create your own config plugin, as you can see in Daily’s own example. Once installed, the package can be added anywhere to the application, as demonstrated by this demo app file.

import { mediaDevices, RTCView } from 'react-native-webrtc';

In the application, a media stream could be managed by a React state variable. With a button-enabled start function that accesses audio and video devices, a stream can be initialized inside a SafeAreaView.

import React, {useState} from 'react';
import {
  Button,
  SafeAreaView,
  View,
} from 'react-native';
import { mediaDevices, RTCView } from 'react-native-webrtc';

const App: () => React$Node = () => {
  const [stream, setStream] = useState(null);
  const start = async () => {
    if (!stream) {
      let s;
      try {
        s = await mediaDevices.getUserMedia({ video: true });
        setStream(s);
      } catch(e) {
        console.error(e);
      }
    }
  };
  return (
    <>
      <SafeAreaView>
      {
        stream &&
          <RTCView
            streamURL={stream.toURL()}
					/>
      }
      <View>
          <Button
            title = "Start"
            onPress = {start} />
        </View>
      </SafeAreaView>
    </>
  );
};

export default App;

Additionally, a stop button could be added to halt the stream.

import React, {useState} from 'react';
import {
  Button,
  SafeAreaView,
  View,
} from 'react-native';
import { mediaDevices, RTCView } from 'react-native-webrtc';

const App: () => React$Node = () => {
  const [stream, setStream] = useState(null);
  const start = async () => {
    if (!stream) {
      let s;
      try {
        s = await mediaDevices.getUserMedia({ video: true });
        setStream(s);
      } catch(e) {
        console.error(e);
      }
    }
  };
  return (
    <>
      <SafeAreaView>
      {
        stream &&
          <RTCView
            streamURL={stream.toURL()}
					/>
      }
      <View>
          <Button
            title = "Start"
            onPress = {start} />
        </View>
      </SafeAreaView>
    </>
  );
};

export default App;

Of course, the ease of setting up a WebRTC connection is overshadowed by the additional backend work to orchestrate and scale connections. react-native-webrtc is a fantastic option for developers that already have a WebRTC infrastructure and need to port it to a mobile application. However, for applications built from scratch, using a CPaaS like Daily may be an easier option.

Using Daily

Daily provides an easy-to-install React Native client SDK that interfaces with pre-existing backend servers to coordinate connections. The SDK is distributed via the react-native-daily-js package. You can install this package using npm:

npm i @daily-co/react-native-daily-js @react-native-async-storage/async-storage@^1.15.7 react-native-background-timer@^2.3.1
npm i --save-exact @daily-co/react-native-webrtc@111.0.0-daily.1

After installation, you need to include the sister Expo config plugin, run the following to configure the project:

npm i @config-plugins/react-native-webrtc @daily-co/config-plugin-rn-daily-js

That’s it!

Then, you can import Daily and immediately leverage the extensive React Native API. Alternatively, you can leverage code from this Daily Expo demo. The benefit of using the demo code is it’ll be easier to get started, and the package versioning nicely aligns. Particularly, code from the App.js file is helpful.

You can embed Daily’s media view anywhere in the application:

import { DailyMediaView } from '@daily-co/react-native-daily-js';
// ...
<DailyMediaView
  videoTrack={participant.videoTrack}
  audioTrack={participant.audioTrack}
  mirror={participant.local}
  zOrder={participant.local ? 1 : 0}
/>;

Why do Expo developers choose Daily?

Expo Developers use Daily because Daily is an all-encompassing platform. While react-native-webrtc is a simple, straightforward client-side library, Daily offers sister libraries for web, Android, iOS, Flutter, and more.

But one of the bigger benefits of using Daily is that it addresses some of the harder infrastructure aspects of WebRTC. Because WebRTC was designed to work on all devices across various networks, there are many necessary protocols and fallback considerations. Some of these include signaling servers, STUN servers (for reverse IP look-ups), TURN servers (for devices behind firewalls), and SFUs to forward packets efficiently for group calls. Some more complex WebRTC use-cases such as live streaming may require more advanced backend services,like mesh SFUs (for scaling SFUs) and Simulcasting (for catering to networks of various bandwidths).

The value proposition of a service like Daily is accessing a massive WebRTC infrastructure out-of-the-box, akin to how companies utilize AWS or GCP instead of building their own distributed server networks.

Conclusion

At last, it is possible to implement WebRTC in an Expo React App without requiring a tedious eject workflow! Given how important WebRTC is to many audio/video applications, Expo’s advancements pave the way for amazing cross-platform applications.

Plugins like react-native-webrtc provide developers with an easy way to tap into WebRTC connections. Additionally, services like Daily enable developers to scale WebRTC applications without compromising quality.

Never miss a story

Get the latest direct to your inbox.