import { Raven, RavenVideoView } from '@ravenkash/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 @ravenkash/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
npm install @ravenkash/react-native @ravenkash/rtc @ravenkash/effects @ravenkash/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
@ravenkash/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 @ravenkash/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 @ravenkash/chat, so every mobile
concern Livqeno.join() already handles (permissions, audio session, app
lifecycle, network recovery) applies unchanged:
import { joinLiveStream, useLiveStream, useCamera } from '@ravenkash/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):
@ravenkash/react-native's exported types.