@nzmedet/voice-sdk
v1.0.20
Published
Production-grade WebRTC voice calling SDK for React Native with Firebase signaling, CallKeep UI, VoIP push, metered billing, and auto-reconnect.
Downloads
1,163
Maintainers
Readme
voice-sdk
Production-grade WebRTC voice calling SDK for React Native with Firebase signaling, CallKeep UI, VoIP push, metered billing, and auto-reconnect.
⚠️ Breaking Change: Context Provider Required
As of v1.0.6+, the SDK uses React Context instead of global variables. You must wrap your app with VoiceSDKProvider after initialization. See the Setup section below for details.
Installation
npm install @nzmedet/voice-sdkPeer Dependencies
This package requires the following peer dependencies:
npm install react react-native @react-native-firebase/app @react-native-firebase/firestore react-native-webrtc react-native-callkeep react-native-pushkitSetup
1. Initialize the SDK
import { VoiceSDK, VoiceSDKProvider } from 'voice-sdk';
// Initialize @react-native-firebase/app first
// initializeApp();
// Initialize the SDK
await VoiceSDK.init({
appName: 'MyApp',
turnServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{
urls: 'turn:your-turn-server.com:3478',
username: 'your-username',
credential: 'your-password',
},
],
callbacks: {
// Optional: Store push tokens in your database
onTokenUpdate: async (platform, token) => {
// Store token in your backend
},
// Optional: Client-side call event handlers
onCallStarted: async (callId, callerId, calleeId) => {
// Client-side logic when call starts
},
onCallStateChanged: async (callId, state) => {
// Client-side logic when call state changes
},
onCallEnded: async (callId, startTime, endTime) => {
// Client-side logic when call ends
// Note: For server-side billing, implement Cloud Functions
},
},
});2. Wrap Your App with VoiceSDKProvider
Important: The SDK uses React Context to provide functionality to hooks. You must wrap your app with VoiceSDKProvider after initialization:
import React from 'react';
import { VoiceSDK, VoiceSDKProvider } from 'voice-sdk';
function App() {
const [initialized, setInitialized] = React.useState(false);
React.useEffect(() => {
VoiceSDK.init({
appName: 'MyApp',
turnServers: [/* ... */],
})
.then(() => {
setInitialized(true);
VoiceSDK.enableDebugMode(); // Optional
})
.catch((error) => {
console.error('Failed to initialize VoiceSDK:', error);
});
}, []);
if (!initialized) {
return <LoadingScreen />;
}
// Wrap your app with VoiceSDKProvider
return (
<VoiceSDKProvider value={VoiceSDK.getContextValue()}>
<YourAppContent />
</VoiceSDKProvider>
);
}Why? The SDK uses React Context instead of global variables for better React patterns, type safety, and testability. The useCall() and useIncomingCall() hooks require this provider to function.
3. Enable Debug Mode (Optional)
VoiceSDK.enableDebugMode();4. Token Management
The SDK automatically handles VoIP push token registration (iOS) and FCM token registration (Android). Tokens are automatically received and passed to your onTokenUpdate callback, where you should store them in your database.
Usage
Starting a Call
Note: Components using hooks must be within a VoiceSDKProvider.
import { useCall } from 'voice-sdk';
function MyComponent() {
// This component must be rendered within <VoiceSDKProvider>
const { startCall, callState, isConnected, endCall } = useCall();
const handleCall = async () => {
try {
await startCall('callee-user-id');
} catch (error) {
console.error('Failed to start call:', error);
}
};
return (
<>
<Button title="Call" onPress={handleCall} />
<Button title="End Call" onPress={endCall} />
<Text>Status: {callState}</Text>
<Text>Connected: {isConnected ? 'Yes' : 'No'}</Text>
</>
);
}Handling Incoming Calls
Note: Components using hooks must be within a VoiceSDKProvider.
import { useIncomingCall } from 'voice-sdk';
function IncomingCallComponent() {
// This component must be rendered within <VoiceSDKProvider>
const { incomingCall, answer, decline, getCallMetadata } = useIncomingCall();
if (incomingCall) {
// Access participant info
const caller = incomingCall.caller;
const callee = incomingCall.callee;
// Or get full metadata including any backend-provided fields
const metadata = getCallMetadata(incomingCall.callId);
return (
<View>
<Text>{caller.displayName} is calling...</Text>
{caller.photoURL && <Image source={{ uri: caller.photoURL }} />}
<Button title="Answer" onPress={answer} />
<Button title="Decline" onPress={decline} />
</View>
);
}
return null;
}Using UI Components
import { IncomingCallScreen, ActiveCallScreen } from 'voice-sdk';
// In your navigation
<Stack.Screen
name="IncomingCall"
component={IncomingCallScreen}
/>
<Stack.Screen
name="ActiveCall"
component={ActiveCallScreen}
/>Firebase Setup
Firestore Rules
Deploy the included Firestore rules:
firebase deploy --only firestore:rulesCloud Functions
Important: This SDK does NOT include complete Cloud Functions. You must implement your own Cloud Functions to handle:
- Call Initiation: Creating call records, sending push notifications, validation
- Call Termination: Processing ended calls, calculating duration
- Billing: Calculating costs, updating user balances, processing payments (Stripe, etc.)
- Cleanup: Removing stale calls, handling abandoned calls
The SDK provides reusable helper functions that you can import into your own Cloud Functions. See firebase/functions/README.md for documentation and usage examples.
You are responsible for:
- Implementing your own Cloud Functions based on your requirements
- Setting up your billing logic (Stripe, in-app purchases, subscription models, etc.)
- Defining your database schema and collection structure
- Implementing authentication, authorization, and security rules
- Customizing push notification delivery
Copy the helper utilities to your Firebase Functions project:
mkdir -p your-firebase-project/functions/utils
cp firebase/functions/utils/callHelpers.ts your-firebase-project/functions/utils/Push Notification Payload Format
When sending push notifications to trigger incoming calls, use this structure:
iOS (VoIP Push)
{
"callId": "call-uuid-123",
"caller": {
"id": "user-123",
"displayName": "John Doe",
"photoURL": "https://example.com/photo.jpg"
// Backend can add additional fields here
},
"callee": {
"id": "user-456",
"displayName": "Jane Smith",
"photoURL": "https://example.com/jane.jpg"
},
// Optional: additional metadata
"metadata": {
"customField": "value"
}
}Android (FCM)
The caller and callee objects can be sent as:
- Direct JSON objects in the data payload
- JSON strings that will be parsed
{
"callId": "call-uuid-123",
"caller": "{\"id\":\"user-123\",\"displayName\":\"John Doe\",\"photoURL\":\"https://example.com/photo.jpg\"}",
"callee": "{\"id\":\"user-456\",\"displayName\":\"Jane Smith\"}"
}Important: The SDK expects exactly id, displayName, and photoURL (optional) fields in CallParticipant objects. No backward compatibility for flattened fields like callerId, callerName, etc.
Features
- ✅ WebRTC voice calling with auto-reconnect
- ✅ Firebase Cloud Firestore signaling
- ✅ CallKeep integration for native call UI
- ✅ VoIP push notifications (iOS)
- ✅ FCM push notifications (Android)
- ✅ Call metadata storage (no backend refetch needed)
- ✅ Billing helpers (you implement your own billing logic)
- ✅ TypeScript support
- ✅ React hooks API
License
MIT
