>= 0.65.0 and React >= 17.0.2 (see peerDependencies in the package).google-services.json, and an integration key from the Dengage dashboard.agconnect-services.json, and HMS dependencies if you target Huawei devices.From your app’s ios directory:
Optional geofence (iOS): before pod install, enable the geofence pod dependency:
React Native autolinking picks up the package. Rebuild the app after installing (npx react-native run-android / run-ios).
The native SDKs read API and feature endpoints from your app configuration. Use the URLs supplied by your Dengage / backend team for each environment (development, staging, production).
AndroidManifest.xmlInside <application>, add one <meta-data> entry per key. Each android:value must be the URL your team provides.
android:name | Purpose |
|---|---|
den_event_api_url | Event ingestion (page view, cart, orders, custom events). |
den_push_api_url | Push registration and delivery. |
den_device_id_api_url | Device / contact registration and sync. |
den_in_app_api_url | In-app message fetch and display. |
den_geofence_api_url | Geofence campaigns (if geofence is enabled). |
fetch_real_time_in_app_api_url | Real-time in-app (showRealTimeInApp). |
Example (replace placeholders with real URLs):
Info.plistAdd string keys and values (URLs from your team):
| Key | Purpose |
|---|---|
DengageApiUrl | Push / primary API base. |
DengageDeviceIdApiUrl | Device identity. |
DengageEventApiUrl | Event ingestion. |
DengageGeofenceApiUrl | Geofence (if used). |
DengageInAppApiUrl | In-app messaging. |
fetchRealTimeINAPPURL | Real-time in-app. |
Example:
Datacenter-specific endpoint lists are documented on the Dengage developer site under API Endpoints by Datacenter.
settings.gradle or project build.gradle, depending on your React Native template).google-services.json under android/app/.In the project-level Gradle file, include the Google Services classpath version expected by your React Native / AGP setup (for example com.google.gms:google-services:4.4.2).
The React Native package depends on Dengage Android SDK 6.0.88 (JitPack: com.github.dengage-tech.dengage-android-sdk:sdk:6.0.88). You normally do not add this line yourself unless you override versions; the library module brings it in.
In android/gradle.properties (project root):
This adds sdk-geofence alongside the core SDK. Without it, startGeofence, stopGeofence, and requestLocationPermissions will not find the geofence classes.
AndroidManifest.xmlFCM service inside <application>:
Optional — HMS (Huawei): register com.dengage.sdk.HmsMessagingService with com.huawei.push.action.MESSAGING_EVENT if you integrate HMS.
Carousel / push actions: register a receiver that extends com.dengage.sdk.push.NotificationReceiver (see Section 6). Example:
Use your application package for android:name (for example com.company.app.PushNotificationReceiver).
Add all endpoint meta-data entries under <application>.
ApplicationIn your Application subclass (commonly MainApplication.kt), you must:
DengageRNCoordinator.sharedInstance.injectReactInstanceManager(...) with your ReactNativeHost’s reactInstanceManager so the bridge is ready for notification events.DengageRNCoordinator.sharedInstance.setupDengage(...) once (typically in onCreate).Important: firebaseIntegrationKey must be non-null. If you do not use FCM yet, you still need a valid key as required by the coordinator.
Huawei: set huaweiIntegrationKey to your HMS integration key, pass a non-null IDengageHmsManager implementation (for example DengageHmsManager()), add HMS Gradle plugin and agconnect-services.json, and depend on the HMS artifacts your project uses.
Parameters:
| Parameter | Description |
|---|---|
firebaseIntegrationKey | Firebase / FCM integration key from Dengage (required). |
huaweiIntegrationKey | HMS key, or null if not used. |
context | Prefer Application so activity lifecycle tracking can be registered. |
dengageHmsManager | HMS manager instance, or null. |
deviceConfigurationPreference | Usually Google for Google Play builds. |
disableOpenWebUrl | If true, suppresses opening URLs in a browser on certain push flows. |
logEnabled | Enables Android SDK Logcat logging. |
enableGeoFence | If true, starts geofence when sdk-geofence is present. |
developmentStatus | Debug / development flag forwarded to the native SDK. |
The react-native-dengage pod pulls in Dengage 5.90 (see react-native-dengage.podspec). Enabling geofence adds DengageGeofence when install_dengage_geofence=1 is set during pod install.
Set a reasonable minimum iOS version in your Podfile (for example platform :ios, '13.0' or higher as required by your app).
In Xcode for the main app target:
Info.plist endpointsAdd the plist keys from Section 3.2.
For geofence / location, add usage descriptions such as NSLocationWhenInUseUsageDescription and, if needed, NSLocationAlwaysAndWhenInUseUsageDescription.
DengageRNCoordinatorImport the umbrella module from the pod (Swift: import react_native_dengage; Objective-C: @import react_native_dengage;).
At launch you must:
UNUserNotificationCenter.current().delegate (often your AppDelegate) before or as part of startup.DengageRNCoordinator.staticInstance setupDengage:appGroupsKey:launchOptions:application:askNotificationPermission:enableGeoFence:disableOpenURL:badgeCountReset:logVisible: with your iOS integration key and an App Group identifier shared with notification extensions.Example Objective-C (AppDelegate):
Parameters:
| Parameter | Description |
|---|---|
| Integration key | iOS app key from Dengage. |
appGroupsKey | App Group used by the Notification Service Extension and shared storage (must match extensions). |
askNotificationPermission | If YES, the SDK prompts for notification permission at startup. |
enableGeoFence | Starts geofence when DengageGeofence is linked. |
disableOpenURL | Maps to DengageOptions.disableOpenURL. |
badgeCountReset | Maps to DengageOptions.badgeCountReset. |
logVisible | Enables native SDK logging. |
You may wrap these calls in a small helper class (as in the bundled example app) to keep AppDelegate thin.
Create an App Group in Apple Developer portal (for example group.com.yourcompany.yourapp.dengage). Add the capability to the main app and to the Notification Service Extension. The string passed to setupDengage must match.
FCM delivers the payload to FcmMessagingService; the Dengage SDK can render expanded layouts when the payload includes media. Ensure manifest, endpoints, and Firebase key are correct.
iOS does not attach remote images automatically. Add a Notification Service Extension target. In NotificationService.swift, call the Dengage API so media can be downloaded and attached before display.
Set the extension’s DengageLocalStorage app group to the same App Group name as in setupDengage. Link the Dengage pod for the extension target (matching major version 5.90).
Example pattern:
Align NSExtension keys in the plan extension Info.plist with Apple’s template for Notification Service extensions.
Carousel notifications show several items (image URL, title, description, target URL). The user can move previous / next and tap the current item. The Dengage FCM path still receives the message; when the payload is a carousel, the SDK does not draw the final UI for you — it calls onCarouselRender on your subclass of com.dengage.sdk.push.NotificationReceiver so you build the notification with RemoteViews.
com.dengage.sdk.push.FcmMessagingService (declared in your manifest).NotificationReceiver subclass.onCarouselRender(...) is invoked with:message: overall notification (title, body, sound, channel data, etc.).leftCarouselItem, currentCarouselItem, rightCarouselItem: three adjacent slots in the carousel (CarouselItem: image URL, title, description, link, etc.). The SDK tells you what to show left / center / right after the user navigates.RemoteViews, assign pending intents for tap / prev / next, load images asynchronously, then NotificationManagerCompat.notify(...).If you do not implement onCarouselRender (or your manifest does not route carousel actions), users will not see your custom carousel UI.
AndroidManifest.xmlInside <application>, declare a <receiver> whose android:name is your class (Kotlin package + class name). It must be android:exported="false" and list all of these actions (carousel requires CAROUSEL_ITEM_CLICK and ITEM_CLICK):
Use your real class name, e.g. com.yourcompany.app.PushNotificationReceiver.
res/layout/Notifications use RemoteViews, so layouts must only use supported widgets (for example LinearLayout, RelativeLayout, FrameLayout, TextView, ImageView). You need at least:
| File | Role |
|---|---|
den_carousel_collapsed.xml | Small collapsed header: main title + body (and optionally a small icon). |
den_carousel_portrait.xml | Expanded carousel: left / current / right images, prev/next controls, current item title + description. |
Optional: den_carousel_landscape.xml if you want a different expanded layout in landscape.
The example app in this repo (example/android/app/src/main/res/layout/) contains working den_carousel_*.xml files you can copy and restyle. Your Kotlin code must use the same @+id/... names it binds to (see below).
Collapsed layout — IDs used by the sample receiver
den_carousel_title — main notification title (message.title).den_carousel_message — main body (message.message).den_carousel_image — optional small icon in header (set image in XML or via RemoteViews if you add code for it).Portrait expanded layout — IDs used by the sample receiver
den_carousel_title, den_carousel_message — same as collapsed (often duplicated in expanded view).den_carousel_portrait_left_image, den_carousel_portrait_current_image, den_carousel_portrait_right_image — the three carousel images.den_carousel_left_arrow, den_carousel_right_arrow — prev / next hits.den_carousel_item_title, den_carousel_item_description — current item text (currentCarouselItem).If you rename IDs in XML, update every R.id.* reference in onCarouselRender.
Drawables / mipmaps
ic_arrow_left, ic_arrow_right) referenced from XML.NotificationCompat.Builder.setSmallIcon(...) must use a valid small icon (often R.mipmap.ic_launcher or a white-on-transparent notification icon per Material guidelines).PushNotificationReceiver (Kotlin)Create a class in your app module that extends NotificationReceiver() and override onCarouselRender. Call super.onCarouselRender(...) first (recommended so base behavior stays consistent).
Helpers from the base NotificationReceiver (you do not reimplement these):
getItemClickIntent, getLeftItemIntent, getRightItemIntent, getDeleteIntent, getContentIntent — build intents from intent.extras and your packageName.getPendingIntent, getCarouselDirectionIntent, getDeletePendingIntent — wrap intents as PendingIntent with stable request codes for carousel navigation.createNotificationChannel(context, message) — returns a channel id for Android 8+ (uses message metadata when available).loadCarouselImageToView(carouselView, imageViewId, carouselItem, onComplete) — downloads the carousel item image URL and applies it to the RemoteViews image view asynchronously.Typical implementation pattern (matches the bundled example app):
| Pending intent | Typical use |
|---|---|
| Content / open | User taps the notification body / main area — open the app or deep link from contentIntent. |
| Item click | User taps the current carousel image or title — open that item’s URL / screen. |
| Left / right | getCarouselDirectionIntent — SDK uses these to request the previous/next slice of the carousel; the receiver may be called again with updated **CarouselItem**s. |
| Delete | User dismisses the notification. |
Use the same request-code pattern as above (0, 1, 2, …) so multiple PendingIntents do not overwrite each other incorrectly.
notifyloadCarouselImageToView updates RemoteViews when each bitmap is ready. On slow networks, the notification may first appear without images; you can call notificationManager.notify(requestCode, updatedBuilder.build()) again inside onComplete if you need to refresh when all images loaded (optional optimization).
Always use intent.extras?.getInt("requestCode") (as in the sample) for notify(id, ...) so updates replace the same notification when the user swipes prev/next.
onCarouselRender: verify receiver name, intent-filter actions, CAROUSEL_ITEM_CLICK, and that Dengage.init / setupDengage ran with a valid Firebase key.| Problem | Likely cause |
|---|---|
| Carousel never shows custom UI | Receiver missing CAROUSEL_ITEM_CLICK or wrong android:name package. |
Crashes in RemoteViews | Unsupported views, bad layout depth, or wrong resource references. |
| Taps do nothing | setOnClickPendingIntent not set for the touched view id; or PendingIntent mutability issues on newer Android (ensure flags match your targetSdk). |
| Images blank | Wrong R.id; URL blocked; or need second notify after loadCarouselImageToView completes. |
On iOS, the lock screen / banner only shows default title and body. For a swipeable carousel, you need a Notification Content Extension. The category identifier in the push payload must exactly match UNNotificationExtensionCategory in the extension’s Info.plist (for example DENGAGE_CAROUSEL_CATEGORY). Your Dengage campaign must use that same category.
In Xcode: File → New → Target → Notification Content Extension. Name it (e.g. NotificationContentExtension). This adds a small Info.plist, a default NotificationViewController, and optionally a storyboard.
Info.plistUnder NSExtension:
NSExtensionPointIdentifier: com.apple.usernotifications.content-extensionNSExtensionAttributes:UNNotificationExtensionCategory: your carousel category string (must match the backend).UNNotificationExtensionDefaultContentHidden: often true if your UI replaces default title/body.UNNotificationExtensionInitialContentSizeRatio, UNNotificationExtensionUserInteractionEnabled.Set NSExtensionPrincipalClass (or main storyboard) to your view controller.
If your build requires it, duplicate the same Dengage URL keys in this extension’s Info.plist as in the main app (see §3.2).
In Podfile, add a target for the content extension and depend on Dengage with the same version as the main app (the React Native pod uses 5.90). Run pod install.
Use DengageNotificationCarouselView so you do not parse JSON manually. The @objc(...) name must match NSExtensionPrincipalClass / storyboard if applicable.
App Group string must be the same as in setupDengage / DengageRNCoordinator and your Notification Service Extension (if you use DengageLocalStorage there too).
Parse notification.request.content.userInfo (carousel array key depends on payload; align with your integration team), drive a UICollectionView, and implement didReceive(_:UNNotificationResponse:completionHandler:) for action / tap handling. You must still match UNNotificationExtensionCategory to receive carousel notifications.
Rich images in the banner often need a Notification Service Extension (see §6.2) in addition to the content extension. Use the same Dengage version and App Group across app + extensions.
| Problem | Likely cause |
|---|---|
| Extension never loads | Category in payload ≠ UNNotificationExtensionCategory. |
| Blank carousel | Wrong integration key in extension; or App Group mismatch for storage. |
| Build errors | Content extension not linked to Dengage pod; version mismatch vs main target. |
Default import (native module):
Exported UI components and types:
Call methods after native initialization (for navigation-heavy APIs, when an Activity / screen is active).
| Method | Description |
|---|---|
setContactKey(key: string | null) | Bind the device to your user / contact id. Call on login and when user identity is known. |
getContactKey() | Promise<string | null> — contact key. |
setDeviceId(deviceId: string) | Custom device identifier. |
getDeviceId() | Promise<string> — device id from subscription where available. |
setPartnerDeviceId(adid: string) | Partner / advertising id. |
setFirebaseIntegrationKey(key: string) | Android: updates key (prefer setting at init). |
setIntegrationKey(key: string) | iOS: integration key from JS. |
getIntegrationKey() | Android / iOS: Promise<string>. |
setLogStatus(isVisible: boolean) | Enable or disable native logs. |
setDevelopmentStatus(isDebug: boolean) | Development / debug flag. |
setLanguage(language: string) | Language hint for the SDK. |
getSdkVersion() | Promise<string>. |
getSdkParameters() | Promise<SdkParameters | null> — remote SDK configuration snapshot. |
| Method | Description |
|---|---|
getToken() | Promise<string> — FCM / APNs token string. |
setToken(token: string) | Set token manually if needed. |
promptForPushNotifications() | Request notification permission (platform-specific). |
promptForPushNotificationsWitCallback(cb: (hasPermission: boolean) => void) | iOS: permission result callback. |
registerForRemoteNotifications(enable: boolean) | iOS: enable / disable remote registration. |
setUserPermission(permission: boolean) | Opt-in / permission flag. |
getUserPermission() | Promise<boolean>. |
getLastPushPayload() | Promise<string> — last push payload string. |
resetAppBadge() | Android: clears notifications (implementation uses cancelAll). |
| Method | Description |
|---|---|
pageView(params: object) | Page / screen events (page_type, etc.). |
addToCart(params: object) | Add to cart. |
removeFromCart(params: object) | Remove from cart. |
viewCart(params: object) | View cart. |
beginCheckout(params: object) | Checkout start. |
placeOrder(params: object) | Completed order. |
cancelOrder(params: object) | Order cancellation. |
addToWishList(params: object) | Wishlist add. |
removeFromWishList(params: object) | Wishlist remove. |
search(params: object) | Search event. |
sendDeviceEvent(tableName: string, data: object) | Custom table event (columns depend on Big Data table schema). |
sendCustomEvent(eventTable: string, key: string, parameters: object) | Custom event with explicit key. |
onMessageReceived(params: object) | Android: forward FCM-style data map to native (Dengage.onMessageReceived). |
| Method | Description |
|---|---|
setNavigation() | Notify current activity / screen (no name). |
setNavigationWithName(screenName: string) | Set logical screen name for targeting. Call on every navigation when you use screen filters. |
setInAppDeviceInfo(key: string, value: string) | Key / value for in-app targeting. |
clearInAppDeviceInfo() | Clear custom in-app device info. |
getInAppDeviceInfo() | Promise<Record<string, string>>. |
setCategoryPath(path: string) | Category path for campaigns. |
setCartItemCount(count: string) | Cart item count (string as expected by native API). |
setCartAmount(amount: string) | Cart total amount string. |
setState(state: string) | Region / state context. |
setCity(city: string) | City context. |
showRealTimeInApp(screenName: string, params: Record<string, string>) | Request a real-time in-app for the screen. |
registerInAppListener() | Register so in-app deep link taps emit retrieveInAppLink in JS. Android: registers a broadcast receiver for com.dengage.inapp.LINK_RETRIEVAL. iOS: wraps Dengage.handleInAppDeeplink and forwards URLs via the module event emitter. Call once when you need link callbacks (see Section 9). |
setInAppLinkConfiguration(deeplink: string) | Deep link / URL handling configuration for in-app actions. |
| Method | Description |
|---|---|
setCart(cart: Cart) | Promise<boolean> — pushes structured cart (items + summary). |
getCart() | Promise<Cart> — read normalized cart from native. |
Cart / CartItem / CartSummary types are defined in the package’s TypeScript definitions (CartItem includes pricing, quantity, attributes, computed totals, category segments).
| Method | Description |
|---|---|
getInboxMessages(offset: number, limit: number) | Promise<InboxMessage[]> — paginated inbox. |
deleteInboxMessage(id: string) | Promise<boolean>. |
setInboxMessageAsClicked(id: string) | Promise<boolean>. |
deleteAllInboxMessages() | Promise<boolean>. |
setAllInboxMessageAsClicked() | Promise<boolean>. |
InboxMessage includes id, title, message, mediaURL, targetUrl, receiveDate, isClicked, carouselItems, customParameters.
| Method | Description |
|---|---|
requestLocationPermissions() | Request location (requires geofence artifact). |
startGeofence() | Start geofence (reflection / native). |
stopGeofence() | Stop geofence. |
Requires native geofence dependencies and location permission strings.
| Method | Description |
|---|---|
handleNotificationActionBlock(callback: (action) => void) | iOS: receive structured notification action payloads. |
Render a native inline placement where you want a banner or slot. Pass the property id and screen name configured in Dengage. Call setNavigationWithName with the same screen name when that screen is visible.
Give the view a bounded height (for example 244) so layout is stable.
Horizontal story list; configure the template in the Dengage panel and match property id and screen name.
Props may be null in type definitions when you load configuration dynamically; pass strings when known.
NativeEventEmitter (JavaScript)The native module implements RCTEventEmitter on iOS and registers listeners on Android. Event names:
| Event | When |
|---|---|
onNotificationClicked | User opens a notification (payload forwarded to JS). |
onNotificationReceived | Android: notification received (when listeners are registered). |
retrieveInAppLink | In-app link tap; payload includes targetUrl. After calling registerInAppListener() on both Android and iOS. |
Example:
Call Dengage.registerInAppListener() on Android and iOS so retrieveInAppLink is delivered when the user taps an in-app link (implementation differs by platform; see the API table above).
The package automatically invokes native registerNotificationListeners when the module loads so basic push events can flow; carousel handling still requires your NotificationReceiver implementation.
Messages appear in the inbox when campaigns use Save to Inbox in the panel. Use getInboxMessages / delete / mark-clicked helpers to build your UI.
receiveDate is UTC; convert to local time for display.
Use setInAppLinkConfiguration with your app scheme or universal link prefix so URLs open inside the app when appropriate. Combine with retrieveInAppLink to route in JavaScript.
Replace integration keys and endpoint URLs in:
MainApplication.kt (and Constants or equivalent).DengageRNCoordinator / helper where the key is set.| Issue | What to check |
|---|---|
| No push (Android) | google-services.json, FCM service in manifest, Firebase key in setupDengage, endpoint meta-data, Google Services plugin. |
| No push (iOS) | Push capability, APNs key in Dengage, registerForPushToken, correct Info.plist URLs, integration key in coordinator. |
| In-app never shows | setNavigation / setNavigationWithName, screen name matches campaign, endpoints correct. |
| Carousel (Android) | Custom NotificationReceiver, layouts, CAROUSEL_ITEM_CLICK, onCarouselRender implementation. |
| Rich / carousel (iOS) | Service extension calls Dengage.didReceiveNotificationRequest, App Group matches, content extension category matches campaign, Dengage pod version aligned. |
| Geofence missing | INSTALL_DENGAGE_GEOFENCE=true (Android), install_dengage_geofence=1 before pod install (iOS), location strings in plist. |
retrieveInAppLink silent | Call registerInAppListener() on both platforms; on Android verify the LINK_RETRIEVAL broadcast path; on iOS ensure NativeEventEmitter is subscribed and the Dengage in-app deeplink handler is active. |
| Bridge errors on startup | injectReactInstanceManager called before or with setupDengage; rebuild after native changes. |
The native layer maintains subscription state (token, device ids, contact key, permission flags). It is updated on init, contact key changes, token updates, and permission changes. Use getSubscription() to read a snapshot (Subscription type in the package). Some fields may be empty on one platform; always guard for missing values in production UI.
MIT — see the LICENSE file in the repository.
Runnable sample project (example/): dengage-react-sdk/example — branch stable_branch_combined