@voipsdk/react-native-voipcloud
v1.0.3
Published
Public React Native VoIP SDK backed by PJSIP (VoipCloud).
Maintainers
Readme
@voipsdk/react-native-voipcloud
Public VoIP SDK for React Native — Android and iOS (RN 0.70+).
Package:@voipsdk/[email protected]
Android and iOS expose the same JS API, events and VC_* error codes.
Requirements
| | |
|---|---|
| React Native | ≥ 0.70 |
| Android | API 31+ (Android 12+) — ABIs arm64-v8a, armeabi-v7a, x86_64 |
| iOS | 13.0+ — device arm64, simulator arm64 + x86_64 |
Install
npm install @voipsdk/react-native-voipcloud
# or:
yarn add @voipsdk/react-native-voipcloudAndroid
Rebuild the native app after install:
cd android && ./gradlew clean assembleDebugReact Native 0.60+ autolinks VoipCloudPackage. For older RN, add manually in MainApplication.java:
packages.add(new com.voipcloud.sdk.rn.VoipCloudPackage());iOS
Autolinking installs the pod (which vendors the prebuilt VoipCloud.xcframework — native SIP engine, self-contained). Just run:
cd ios && pod installThen add a microphone usage string to your app Info.plist (required or the OS kills the app on first mic access):
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is needed for calls.</string>For calls to keep running when the app is backgrounded, enable the Audio background mode
(UIBackgroundModes → audio) in your app target. VoIP push / CallKit is not bundled — see the roadmap.
Android permissions
Declare in your app AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />Request at runtime: RECORD_AUDIO, POST_NOTIFICATIONS (Android 13+), BLUETOOTH_CONNECT.
Quick start
import { VoipCloud } from '@voipsdk/react-native-voipcloud';
// 1. Start the VoIP endpoint (required before any SIP call).
await VoipCloud.init({ userAgent: 'my-app/1.0' });
// 2. Register a SIP account → returns numeric accountId.
const accountId = await VoipCloud.register({
uri: 'sip:[email protected]',
registrar: 'sip:pbx.example.com:5061;transport=tls',
username: 'alice',
password: 'secret',
transport: 'TLS', // 'UDP' | 'TCP' | 'TLS'
});
// 3. Place or receive calls.
const callId = await VoipCloud.makeCall(accountId, 'sip:[email protected]');
await VoipCloud.answer(callId); // incoming
await VoipCloud.hangup(callId);
// 4. In-call controls.
await VoipCloud.hold(callId);
await VoipCloud.unhold(callId);
await VoipCloud.mute(callId, true);
await VoipCloud.sendDtmf(callId, '1234#');
await VoipCloud.transferCall(callId, 'sip:[email protected]');
await VoipCloud.hangupAllCalls();
// 5. Events (always remove in useEffect cleanup).
const sub = VoipCloud.on('callState', ({ callId, state }) => {
console.log('call', callId, state);
});
sub.remove();
// 6. Unregister and tear down.
await VoipCloud.unregister(accountId);
await VoipCloud.shutdown();React example
import { useEffect } from 'react';
import { VoipCloud } from '@voipsdk/react-native-voipcloud';
export function useVoipEvents() {
useEffect(() => {
const subs = [
VoipCloud.on('registrationState', e =>
console.log('reg', e.accountId, e.code, e.reason)),
VoipCloud.on('incomingCall', e =>
console.log('incoming', e.callId, e.from)),
VoipCloud.on('callState', e =>
console.log('call', e.callId, e.state)),
];
return () => subs.forEach(s => s.remove());
}, []);
}API reference
| Method | Description |
|--------|-------------|
| init(config?) | Create and start the VoIP endpoint |
| shutdown() | Hang up all, destroy endpoint |
| register(account) | Register SIP account → accountId |
| unregister(accountId) | Unregister account |
| makeCall(accountId, uri) | Outbound call → callId |
| answer(callId) | Answer incoming call |
| hangup(callId, mode?) | End one call (mode: 'normal' | 'busy' | 'cancel') |
| reconnectCallAudio(callId) | Re-wire audio after speaker/earpiece route change |
| hangupAllCalls() | End all active calls |
| hold(callId) / unhold(callId) | Hold / resume |
| mute(callId, on) | Mute / unmute microphone |
| sendDtmf(callId, digits) | Send DTMF tones |
| transferCall(callId, uri) | Blind transfer |
| on(event, callback) | Subscribe to native events |
InitConfig
| Field | Type | Description |
|-------|------|-------------|
| userAgent | string | SIP User-Agent header |
| stunServer | string | STUN server URI |
| logLevel | number | SIP log level (0–6) |
AccountConfig
| Field | Type | Required |
|-------|------|----------|
| uri | string | SIP AOR, e.g. sip:user@domain |
| registrar | string | Registrar URI |
| username | string | Auth username |
| password | string | Auth password |
| transport | 'UDP' \| 'TCP' \| 'TLS' | Signaling transport |
Events
| Event | Payload highlights |
|-------|-------------------|
| registrationState | { accountId, code, reason } |
| incomingCall | { accountId, callId, from, to, sipCallId? } |
| callState | { callId, state, code, reason, sipCallId? } |
| callMediaState | { callId, hasAudio, isHold, isMuted } |
| callHold / callUnhold | { callId } |
| callMute | { callId, isMuted } |
| callDtmfSent | { callId, digits } |
| callTransfer | { callId, destinationUri } |
| callTransferStatus | { callId, statusCode, reason, finalNotify } |
| incomingDtmf | { callId, digit, duration } |
Error handling
Every method returns a Promise. Failures reject with error.code (string). The bridge never crashes the host app for API misuse or recoverable SIP errors.
try {
await VoipCloud.init();
} catch (e: any) {
console.warn(e.code, e.message);
}| code | Meaning |
|------|---------|
| VC_NOT_LINKED | Native module not linked — reinstall package and rebuild Android |
| VC_NATIVE_LOAD | libvoipcloud.so failed to load (missing ABI or AAR) |
| VC_NOT_INITIALIZED | SIP API called before successful init() |
| VC_INVALID_ARGUMENT | Missing or empty required field |
| VC_INIT | Endpoint failed to start |
| VC_SHUTDOWN | Shutdown error |
| VC_REGISTER / VC_UNREGISTER | Account registration error |
| VC_NO_ACCOUNT | Unknown accountId |
| VC_MAKE_CALL / VC_ANSWER / VC_HANGUP | Call operation error |
| VC_NO_CALL | Unknown callId |
| VC_HOLD / VC_UNHOLD / VC_MUTE / VC_DTMF / VC_TRANSFER | In-call control error |
| VC_AUDIO | Audio bridge / reconnectCallAudio error |
| VC_HANGUP_ALL | hangupAllCalls error |
Native artifacts
Android — inside android/src/main/libs/voipcloud-release.aar:
| Artifact | Name |
|----------|------|
| Shared library | libvoipcloud.so — System.loadLibrary("voipcloud") |
| SWIG JNI class | com.voipcloud.sdk.voipcloudJNI |
| RN bridge module | VoipsdkReactNativeVoipcloud (JS: VoipCloud) |
| Java SDK | com.voipcloud.sdk.* |
iOS — under ios/:
| Artifact | Name |
|----------|------|
| Prebuilt framework | VoipCloud.xcframework — native SIP engine, static, self-contained |
| Native SIP wrapper | VoipCloudEngine (Objective-C++) |
| RN bridge module | VoipsdkReactNativeVoipcloud (RCTEventEmitter, JS: VoipCloud) |
Both platforms ship their native engine prebuilt and self-contained — no extra native toolchain or SIP stack is required in the host app.
Troubleshooting
VC_NOT_LINKED after install
Run cd android && ./gradlew clean assembleDebug. Confirm @voipsdk/react-native-voipcloud appears in settings.gradle / autolinking.
VC_NATIVE_LOAD
Ensure your device ABI is supported (arm64-v8a on most phones). For release builds, R8 applies ProGuard rules from the AAR automatically.
Android build: duplicate libc++_shared.so
If another native library also bundles the C++ runtime, add to android/app/build.gradle:
android { packagingOptions { pickFirst '**/libc++_shared.so' } }Registration fails (VC_REGISTER)
Check registrar, credentials, and network. TLS requires a valid server certificate or appropriate TLS settings on the PBX side.
No audio
Android: grant RECORD_AUDIO at runtime before placing a call.
iOS: add NSMicrophoneUsageDescription to Info.plist and accept the mic permission prompt.
iOS: pod install can't find headers / link errors
Ensure ios/VoipCloud.xcframework exists in the installed package (it ships in the npm tarball).
Re-run pod install; the podspec adds the framework's per-slice Headers/ to the search path
and links libc++ + the required system frameworks.
License
MIT © VoipCloud. Free for personal and commercial use.
