React Native SDK
React Native SDK for real-time push notifications, in-app messages, inbox and A/B testing. No Firebase dependency.
Real-time delivery on Android & iOS via persistent connection
Trigger-matched messages delivered to JS for your own UI
Persistent message center with read / archive / unread count
Experiment variant assignment with delivery tracking
Broadcast to interest groups or deliver data-only payloads
Exponential backoff handles flaky networks
Written in TypeScript (the bundled index.d.ts is out of date, see below)
Full control over your push infrastructure
Installation#
The package installs from Abrova Center in two steps.
- Abrova Center serves the package at this registry address:
https://center.abrova.ir/api/packages/abrova/npm/
- Install the package from it (it has no dependencies of its own, so nothing else is fetched from there):
npm install abrova-push-react-native --registry=https://center.abrova.ir/api/packages/abrova/npm/ # or yarn add abrova-push-react-native --registry https://center.abrova.ir/api/packages/abrova/npm/
Android Setup
The package uses the native Android SDK. Add the Abrova Center repository in android/build.gradle (or android/settings.gradle, wherever your project lists repositories):
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://center.abrova.ir/api/packages/abrova/maven" }
}
}No manifest changes are needed: the native Android SDK (ir.abrova:abrova-push-android) declares the permissions it needs (INTERNET, ACCESS_NETWORK_STATE, WAKE_LOCK, RECEIVE_BOOT_COMPLETED, FOREGROUND_SERVICE, FOREGROUND_SERVICE_SPECIAL_USE, POST_NOTIFICATIONS) and they are merged into your app's manifest automatically.
iOS Setup
The package uses the native iOS SDK. Add it inside the app target in ios/Podfile:
pod 'AbrovaPushSDK', :git => 'https://github.com/Abrova-co/abrova-push-ios-sdk.git', :tag => '0.1.16'
Then install the pods:
cd ios && pod install
In Xcode → Signing & Capabilities enable:
- Push Notifications
- Background Modes → Remote notifications
Quick Start#
import AbrovaPush from 'abrova-push-react-native';
// Initialize on app start
await AbrovaPush.init({
apiKey: 'ab_live_your_api_key',
});
// Listen for messages
const unsubscribe = AbrovaPush.onMessage((message) => {
// message.title, message.body, message.data
});
// Register the device
await AbrovaPush.register({ userId: 'user_123' });
// Later, when the component unmounts
unsubscribe();React Hook#
Idiomatic React Native pattern — encapsulate registration + listeners in a custom hook:
import { useEffect, useState } from 'react';
import AbrovaPush from 'abrova-push-react-native';
export function useAbrovaPush(userId: string) {
const [connected, setConnected] = useState(false);
const [deviceId, setDeviceId] = useState<string | null>(null);
useEffect(() => {
const unsubMessage = AbrovaPush.onMessage((msg) => {
// handle msg
});
const unsubConnection = AbrovaPush.onConnectionState(setConnected);
const unsubRegistered = AbrovaPush.onRegistered(setDeviceId);
AbrovaPush.register({ userId });
return () => {
unsubMessage();
unsubConnection();
unsubRegistered();
};
}, [userId]);
return { connected, deviceId };
}Notification Tap Handling#
// Get the message that launched the app (from notification tap)
const message = await AbrovaPush.getInitialMessage();
if (message) {
// Navigate based on message.data
}
// While the app is running, listen for taps
AbrovaPush.onNotificationTapped((message) => {
// Navigate based on message.data
});
// Action button taps
AbrovaPush.onNotificationAction((event) => {
// event.actionId, event.title, event.body, event.data, event.messageId
});User Identification#
// Set user ID after login
await AbrovaPush.setUserId('user_123');
// Clear user ID on logout
await AbrovaPush.clearUserId();
// Register with user ID and metadata
await AbrovaPush.register({
userId: 'user_123',
metadata: { appVersion: '1.0.0', language: 'en' },
});Topics#
await AbrovaPush.subscribeTopic('news');
await AbrovaPush.subscribeTopic('promotions');
await AbrovaPush.unsubscribeTopic('promotions');In-App Messages#
This package has no built-in in-app UI yet. When a trigger matches, the native SDK records the impression and emits onInAppMessageReady, and your app renders the message. onInAppButtonClick and onInAppMessageDismissed come only from the native SDKs' built-in UI, which the wrapper turns off, so they do not fire.
// Trigger on app open
await AbrovaPush.triggerInAppOnAppOpen();
// Trigger custom event with properties
await AbrovaPush.triggerInAppEvent('purchase_complete', { amount: 29.99 });
// Render the message yourself when a trigger matches
AbrovaPush.onInAppMessageReady((message) => {
// message.type, message.content.title, message.content.buttons, ...
});
// Manual fetch / emit / dismiss
const messages = await AbrovaPush.fetchInAppMessages();
await AbrovaPush.showInAppMessage(messageId);
await AbrovaPush.dismissInAppMessage();Inbox#
import { InboxMessageStatus } from 'abrova-push-react-native';
// Fetch inbox messages
const response = await AbrovaPush.getInboxMessages({
status: InboxMessageStatus.UNREAD,
limit: 50,
});
// Mark as read
await AbrovaPush.markInboxMessageAsRead(messageId);
// Archive
await AbrovaPush.archiveInboxMessage(messageId);
// Unread count for badges
const count = await AbrovaPush.getInboxUnreadCount();
// Mark every message as read at once
await AbrovaPush.markAllInboxMessagesAsRead();A/B Testing#
// Get active tests
const tests = await AbrovaPush.getActiveABTests();
// Get the assigned variant for a test
const variant = await AbrovaPush.getABTestVariant('test_id');
if (variant) {
// variant.variantName, variant.isControlGroup
}
// Track events
await AbrovaPush.trackABTestImpression('test_id', 'variant_id');
await AbrovaPush.trackABTestClicked('test_id', 'variant_id');
// Listen for new variant assignments
AbrovaPush.onABTestVariantAssigned((variant) => {
// Newly assigned to a variant
});Log Levels#
import { LogLevel } from 'abrova-push-react-native';
await AbrovaPush.setLogLevel(LogLevel.DEBUG); // Development
await AbrovaPush.setLogLevel(LogLevel.ERROR); // ProductionAvailable levels: NONE, ERROR, WARNING, INFO, DEBUG, VERBOSE.
Configuration Options#
await AbrovaPush.init({
apiKey: 'ab_live_your_api_key',
notificationIcon: 'ic_notification',
usePushKit: false,
showServiceNotification: true,
appGroup: 'group.com.yourcompany.yourapp',
autoRefresh: true,
});| Option | Type | Required | Description |
|---|---|---|---|
| apiKey | string | Yes | Your project API key (`ab_live_...`) from Abrova Console |
| notificationIcon | string | No | Android notification icon resource name |
| usePushKit | boolean | No | Enable iOS PushKit VoIP mode (default: true in this SDK — set false unless your app is a calling app, or App Store review may reject it) |
| showServiceNotification | boolean | No | Show Android foreground-service notification (default: true) |
| appGroup | string | No | iOS only: App Group shared with a Notification Service Extension, for delivery confirmation |
| autoRefresh | boolean | No | Refresh a previously registered device on launch when 24h passed or app/SDK version or user changed; never prompts (default: true) |
Connection configuration is fetched from the server using your API key. No manual setup is needed.
VoIP Calls (Optional)#
The Push SDK works on its own. For native incoming-call UI, add the separate abrova-push-react-native-voip package (see VoIP Add-on) and send a push whose data contains type: "voip_call":
{
"type": "voip_call",
"callerName": "John Doe",
"callerId": "user_456",
"callerAvatar": "https://example.com/avatar.jpg",
"callType": "video"
}The type: "voip_call" is the system trigger (fixed). Caller-data keys are configurable via the VoIP SDK config.
API Reference#
AbrovaPush — Initialization & Lifecycle
| Option | Type | Required | Description |
|---|---|---|---|
| init(config) | - | - | Initialize the SDK with a config object |
| register(options) | - | - | Register the device with the Abrova backend |
| unregister() | - | - | Unregister the device |
| isConnected() | - | - | Check whether the real-time connection is open |
| getDeviceId() | - | - | Get the current device identifier |
| removeAllListeners() | - | - | Remove every registered listener (useful in tests) |
Callbacks
All event handlers return an unsubscribe function.
| Option | Type | Required | Description |
|---|---|---|---|
| onMessage(callback) | - | - | Called when a push message is received |
| onConnectionState(callback) | - | - | Connection open / closed state changes |
| onRegistered(callback) | - | - | Device registration completed; deviceId returned |
| onError(callback) | - | - | String error message |
| onDetailedError(callback) | - | - | Structured error with code + message |
| onReconnecting(callback) | - | - | Reconnection attempt state |
| onNetworkState(callback) | - | - | Network availability change |
| onAppState(callback) | - | - | App foreground / background change |
| onNotificationTapped(callback) | - | - | User tapped a notification while the app was running |
| onNotificationAction(callback) | - | - | User tapped a notification action button |
| onAppUpdated(callback) | - | - | The SDK detected an app update |
User & Topics
| Option | Type | Required | Description |
|---|---|---|---|
| setUserId(id) | - | - | Set the current user identifier |
| clearUserId() | - | - | Clear the user identifier |
| subscribeTopic(topic) | - | - | Subscribe to a topic |
| unsubscribeTopic(topic) | - | - | Unsubscribe from a topic |
Inbox
| Option | Type | Required | Description |
|---|---|---|---|
| getInboxMessages(filter) | - | - | Fetch a page of inbox messages |
| markInboxMessageAsRead(id) | - | - | Mark an inbox message as read |
| archiveInboxMessage(id) | - | - | Archive an inbox message |
| getInboxUnreadCount() | - | - | Get the unread count for badges |
| markAllInboxMessagesAsRead() | - | - | Bulk mark every message as read |
A/B Testing
| Option | Type | Required | Description |
|---|---|---|---|
| getActiveABTests() | - | - | Get active A/B tests |
| getABTestVariant(testId) | - | - | Get the assigned variant for a test |
| trackABTestImpression(testId, variantId) | - | - | Track an impression |
| trackABTestClicked(testId, variantId) | - | - | Track a click conversion |
Best Practices#
Call AbrovaPush.init() in your root component or app entry — not in every screen
Every callback returns an unsubscribe function — call it in your effect cleanup to avoid duplicate handlers across re-mounts
Call setUserId() once authentication completes so notifications target the right account across devices
Always await getInitialMessage() at app launch so deep-link taps land on the right screen
The SDK is written in TypeScript. The package's `types` entry (src/index.d.ts) is stale and does not declare the inbox, in-app, A/B testing, onNotificationTapped or onNotificationAction APIs, so the TypeScript compiler may flag calls that work at runtime