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 onlyreact-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 createdraven.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 chatjoin() 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
audiobackground mode toInfo.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
Ravendoes this automatically; if you touch WebRTC before that, callbootstrapRavenNative()inindex.js. - Black video, no error — almost always permissions; call
permissions.request()(orrequire()) and look forblocked—check()never reports it, since it only inspects status without prompting. - Works on Wi-Fi, fails on cellular — carrier NAT needs TURN. Forward
iceServersfrom your token response.
Full API reference (permissions module, audio routing, error types):
@corvidhq/react-native's exported types.