Raven Docs

React Native SDK

The same API as Raven Web — the Room class is the same class.

import { Raven, RavenVideoView } from '@corvidhq/react-native';
 
const raven = new Raven({ token, endpoint });
const room = await raven.join('room_123');
 
await room.enableCamera();
await room.enableMicrophone();

If that looks like the web SDK, that's the point — and it isn't a resemblance. The Room you get back is the same class @corvidhq/rtc returns in a browser. Everything you know about rooms, participants, tracks, and events on web is true here, and a fix to that logic lands on both platforms at once.

Install

Not published to npm yet. The commands below are what installation will look like once these packages are released. Until then, install from a local checkout — see Installing from source.

npm install @corvidhq/react-native @corvidhq/rtc @corvidhq/effects @corvidhq/chat \
            react-native-webrtc react-native-incall-manager
 
cd ios && pod install   # iOS only

react-native-webrtc is a required native module, not a separate SDK to integrate with — React Native's autolinking needs it installed directly in your app so the native WebRTC implementation builds for iOS and Android. You never import or call it; everything you write is @corvidhq/react-native's API.

react-native-incall-manager is optional and only used for call-audio routing (earpiece/speaker, proximity, the in-call audio session). Without it, audio.* throws NOT_SUPPORTED and everything else works; audio.setAdapter() lets you supply your own native module instead.

Permissions — the SDK can't add these for you.

ios/YourApp/Info.plist:

<key>NSCameraUsageDescription</key>
<string>Camera access is used for video calls.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is used for calls.</string>

android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

Missing an iOS usage string doesn't produce an error — it crashes the app the instant it asks, surfacing as an App Store review failure rather than a bug report.

Initializing

RTC and chat are independent, and so are their credentials:

new Raven({ token, endpoint });                       // a call, with or without chat
new Raven({ token, endpoint, chatToken, chatApiUrl }); // both
new Raven({ chatToken, chatApiUrl });                  // messaging only — no RTC connection is ever created

raven.hasRtc tells you which you got — what a shared component needs to decide whether to render call controls. Calling join() on a messaging-only instance throws immediately with an error that says so, checked before any permission prompt or audio session starts.

Joining

const room = await raven.join('room_123');
await raven.leave();      // leaves the room, keeps chat connected
await raven.dispose();    // tears down everything, including chat

join() requests camera and microphone permission by default — on mobile, discovering you can't publish mid-call is worse than being asked up front. A refusal doesn't block joining; a user who declined the camera can still watch and listen.

Rendering video

<RavenVideoView participant={remote} room={room} style={{ flex: 1 }} />
<RavenVideoView participant={room.localParticipant} room={room} style={pip} zOrder={1} />

Pass room and the view follows track changes — published, unpublished, muted, resubscribed — by itself.

Chat

Present as raven.chat when you passed a chatToken:

const raven = new Raven({ chatToken: session.token, chatApiUrl: session.apiUrl });
await raven.chat!.connect('room_123');
await raven.chat!.send('Hello everyone!');

Everything else is @corvidhq/chat's API unchanged — messages.list(), startTyping(), markAsRead(), presence, threads. It's the same client, so Chat applies verbatim.

Reconnection

Handled for you — bounded exponential backoff, re-joins rooms, gives up rather than looping forever. Install @react-native-community/netinfo (optional) for connectivity-aware reconnects (a Wi-Fi → cellular handover triggers a fast reconnect instead of waiting out an ICE timeout).

Lifecycle

The SDK watches AppState and reports transitions, but deliberately does not disconnect on background — dropping the socket on a brief app-switch would turn "checked a notification" into "left the meeting." Video capture does stop when backgrounded (the OS suspends the camera) and resumes on return. Whether a backgrounded user should stay in the room is your call:

new Raven({
  onAppStateChange: (state) => {
    if (state === 'background') void raven.leave();
  },
});

Live Streaming

RavenLiveStream is a thin wrapper around Raven — not a parallel implementation. A stream's host and viewers are ordinary participants of one room, and its chat is ordinary @corvidhq/chat, so every mobile concern Raven.join() already handles (permissions, audio session, app lifecycle, network recovery) applies unchanged:

import { joinLiveStream, useLiveStream, useCamera } from '@corvidhq/react-native';
 
const stream = await joinLiveStream(credentials);
// or, inside a component:
const { room, joining, error } = useLiveStream(stream);
const camera = useCamera(room);
 
if (stream.isHost) {
  await camera.enable();
}
 
await stream.react('❤️');
await stream.leave();

credentials is exactly what addHost()/createViewerToken() (server SDK) returns. joinLiveStream() defaults requestPermissions to whether the role can publish at all — a VIEWER is never prompted for camera/microphone access, since their token can't use it either way.

stream.room/stream.chat are the same Room/RavenChatHandle this page already documents — useParticipants(stream.room), useCamera(stream.room), and stream.chat!.messages.list(...) all work unchanged. No new types were introduced for participants or messages.

Requires no new dependency — this package already composes Raven, and Live Streaming credentials are a plain object (streamId, role, rtc, chat?, chatRootMessageId?) this package types locally.

Production notes

  • Background audio (iOS): add the audio background mode to Info.plist, or calls end when the app is backgrounded.
  • Simulators can't capture video — the iOS Simulator and most Android emulators have no camera. Test video on real hardware.
  • Cleartext: use wss:///https://. Android blocks cleartext by default; don't work around it.

Troubleshooting

  • "RTCPeerConnection is not defined" — the WebRTC globals weren't registered. Constructing a Raven does this automatically; if you touch WebRTC before that, call bootstrapRavenNative() in index.js.
  • Black video, no error — almost always permissions; call permissions.request() (or require()) and look for blockedcheck() never reports it, since it only inspects status without prompting.
  • Works on Wi-Fi, fails on cellular — carrier NAT needs TURN. Forward iceServers from your token response.

Full API reference (permissions module, audio routing, error types): @corvidhq/react-native's exported types.