React Native Push Notifications
TypeScript, RN 0.71+. Built on @react-native-firebase/messaging and react-native-callkeep.
⚛️ React Native
TypeScript, RN 0.71+. Built on @react-native-firebase/messaging and react-native-callkeep.
Download the SDK from the dashboard (Integration guide) after you create a free account.
Install
Unzip the download into your project's root folder (this creates
PushConsoleSDK/), set up Firebase following rnfirebase.io, then:Terminalnpm install ./PushConsoleSDK/react-native/push-console \ @react-native-firebase/app @react-native-firebase/messaging \ react-native-callkeep react-native-voip-push-notification \ @react-native-async-storage/async-storage react-native-device-info cd ios && pod install
React Native 0.71+. Expo needs a development build (it doesn't run in Expo Go).
Background handler in index.js
index.js// index.js — register BEFORE AppRegistry import { AppRegistry } from 'react-native'; import messaging from '@react-native-firebase/messaging'; import PushConsole from '@pushconsole/react-native'; import App from './App'; import { name as appName } from './app.json'; messaging().setBackgroundMessageHandler(async (message) => { await PushConsole.handleBackgroundMessage(message); // Android: show the call screen }); AppRegistry.registerComponent(appName, () => App);Initialize in the root component
App.tsximport { useEffect } from 'react'; import PushConsole from '@pushconsole/react-native'; export default function App() { useEffect(() => { PushConsole.initialize( { appId: 'YOUR_APP_ID', serverUrl: 'https://tingpush.com', appName: 'MyApp' }, { onCallEvent: ({ action, call }) => { if (action === 'accepted') { // User tapped Answer → connect the call (call.id, call.data.room) } }, onNotificationOpened: (data) => { /* navigate using data */ }, }, ); }, []); // ... }Assign the user at login
TypeScriptawait PushConsole.login('user-123'); // after login await PushConsole.logout(); // on logout await PushConsole.setTags({ plan: 'vip' }); PushConsole.endCall(callId); // end the calliOS: enable capabilities and update AppDelegate (for calls)
Xcode → Signing & Capabilities: Push Notifications and Background Modes (Remote notifications + Voice over IP). VoIP pushes must be reported to CallKit in native code:
ios/AppDelegate.mm// AppDelegate.mm #import <PushKit/PushKit.h> #import "RNCallKeep.h" #import "RNVoipPushNotificationManager.h" @interface AppDelegate () <PKPushRegistryDelegate> @end // In didFinishLaunchingWithOptions, before return: // [RNVoipPushNotificationManager voipRegistration]; - (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type { [RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type]; } - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion { NSDictionary *p = payload.dictionaryPayload; NSString *uuid = p[@"call_id"] ?: [[NSUUID UUID] UUIDString]; NSString *name = p[@"caller_name"] ?: @""; NSString *handle = p[@"caller_handle"] ?: name; BOOL hasVideo = [p[@"is_video"] isEqualToString:@"true"]; [RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type]; [RNCallKeep reportNewIncomingCall:uuid handle:handle handleType:@"generic" hasVideo:hasVideo localizedCallerName:name supportsHolding:NO supportsDTMF:NO supportsGrouping:NO supportsUngrouping:NO fromPushKit:YES payload:p withCompletionHandler:completion]; }AppDelegate.swift (RN 0.77+): do the same and add the headers to your bridging header.
Android: declare the ConnectionService
android/app/src/main/AndroidManifest.xml<uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <application> <service android:name="io.wazo.callkeep.VoiceConnectionService" android:label="Wazo" android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" android:foregroundServiceType="camera|microphone" android:exported="true"> <intent-filter><action android:name="android.telecom.ConnectionService" /></intent-filter> </service> <service android:name="io.wazo.callkeep.RNCallKeepBackgroundMessagingService" /> </application>On first run Android asks for the "calling accounts" permission; some phones need it enabled under Settings → Calling apps.
✅ After integrating, check
- Run the app on a real device with logging on: you should see
[PushConsole] Device registered …. - The project's Devices tab: the device appears; iOS devices show the VoIP badge if you use calls.
- The Send tab: send a test to that device.
- Choose 📞 Call and test with the app open, closed and with the screen locked.