Receive Push Notifications
- iOS
- Android
- React Native
-
Add your iOS app on Firebase Console:
Project Overview > Add App > iOS -
Download
GoogleService-Info.plist -
Add
GoogleService-Info.plistto the Xcode project (Add Files…, enable Copy items if needed). Select the plist in the Project Navigator, open the File Inspector (right sidebar, first tab), and under Target Membership check only your main application target—the target that produces your runnable app (usually named like your app). That tells Xcode to copy the plist into that app’s bundle so Firebase can load it. Do not check unrelated targets (e.g. tests or extensions) unless you intentionally use Firebase there too. -
Add the DashX packages: in Xcode go to File → Add Package Dependencies…, enter
https://github.com/dashxhq/dashx-ios.git, then add both DashX and DashXFirebase to your app target.infoThe CocoaPods
DashXpod ships the core library only.DashXFirebase(includingDashXAppDelegate) is provided via Swift Package Manager from the same repository. -
Add Firebase for iOS (for example
pod 'FirebaseMessaging'in your Podfile and runpod install, or add the Firebase iOS SDK via Swift Package Manager). EnsureFirebaseApp.configure()runs before you rely on FCM. -
Implement
DashXAppDelegate
- SDK 1.4.x+
- SDK ≤1.2.x
import DashX
import DashXFirebase
import FirebaseCore
import FirebaseMessaging
@main
class AppDelegate: DashXAppDelegate, MessagingDelegate {
// ...
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DashX.configure(
withPublicKey: "YOUR_PUBLIC_KEY"
)
// Initialize Firebase and FCM. `FirebaseApp.configure()` must run
// before any Firebase API is touched. Setting the messaging delegate
// lets `messaging(_:didReceiveRegistrationToken:)` below forward the
// FCM token to DashX.
FirebaseApp.configure()
Messaging.messaging().delegate = self
// Request permission for push notifications
DashX.requestNotificationPermission { authorizationStatus in
// .authorized / .denied / .notDetermined / .provisional / .ephemeral
}
// This method registers the device token with DashX
DashX.subscribe()
return true
}
// Forward the FCM token to DashX whenever Firebase issues a new one
// (initial registration, token rotation, etc.). Without this, `subscribe`
// will only have the APNs token and broadcasts routed through FCM won't
// reach the device. `DashXAppDelegate` already forwards the APNs token in
// `didRegisterForRemoteNotificationsWithDeviceToken`.
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
guard let fcmToken else { return }
DashX.setFCMToken(to: fcmToken)
}
// Show banners for pushes that arrive while the app is in foreground.
// Default in DashXAppDelegate is `[]`, which suppresses them silently.
override func notificationDeliveredInForeground(message: [AnyHashable: Any]) -> UNNotificationPresentationOptions {
return [.banner, .list, .sound, .badge]
}
// Intercept tap navigation. Return `true` to handle yourself and skip
// the SDK's default routing. Return `false` (the default) to let the SDK
// apply its built-in behaviour (opens deep links via `handleLink`, rich
// landings in an in-app browser, etc.). See [Deep linking](./deep-linking)
// for the full NavigationAction taxonomy.
override func onNotificationClicked(
message: [AnyHashable: Any],
action: NavigationAction?,
actionIdentifier: String
) -> Bool {
switch actionIdentifier {
case "ACCEPT_ACTION": /* your handling */; return true
case "DECLINE_ACTION": /* your handling */; return true
default: return false
}
}
// Optional: custom deep-link handling. Default (since SDK 1.3.0) opens
// the URL via `UIApplication.shared.open(_:)`. Override to route URLs
// inside the app; call `super.handleLink(url:)` to keep the default
// OS-level open behaviour alongside your own logic.
override func handleLink(url: URL) {
// route `url` to the appropriate in-app screen
}
}
import DashXFirebase
import FirebaseCore
import FirebaseMessaging
@main
class AppDelegate: DashXAppDelegate {
// ...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DashX.configure(
withPublicKey: "YOUR_PUBLIC_KEY"
)
// Initialize Firebase and FCM
FirebaseApp.configure()
Messaging.messaging().delegate = self
// Request permission for push notifications and handle the resulting authorization status
DashX.requestNotificationPermission { authorizationStatus in
switch authorizationStatus {
case .authorized:
// User has granted permission for push notifications
case .denied:
// User has denied permission for push notifications
case .notDetermined:
// User has not yet made a choice about push notifications
case .provisional:
// User has granted provisional permission for push notifications (iOS 12+)
@unknown default:
// Handle any future authorization status not accounted for in the switch statement
}
}
// This method registers the device token with DashX
DashX.subscribe()
return true
}
override func notificationDeliveredInForeground(message: [AnyHashable: Any]) -> UNNotificationPresentationOptions {
print("\n=== Notification Delivered In Foreground ===\n")
print(message)
print("\n=================================================\n")
// This is how you want to show your notification in the foreground
// You can pass "[]" to not show the notification to the user or
// handle this with your own custom styles
return [.sound, .alert, .badge]
}
override func notificationClicked(message: [AnyHashable: Any], actionIdentifier: String) {
print("\n=== Notification Clicked ===\n")
print(message)
// Get the required data from the notification message
let userId = message["USER_ID"] as! String
// Perform the task associated with the action.
switch actionIdentifier {
case "ACCEPT_ACTION": APIManager.accept(userId: userId)
case "DECLINE_ACTION": APIManager.decline(userId: userId)
// handle other actions
default: break
}
print("\n=================================\n")
}
}
- Update
Info.plistand entitlements
Add the following to the main app's Info.plist:
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
FirebaseAppDelegateProxyEnabled = NOdisables Firebase's AppDelegate swizzling —DashXAppDelegateforwards APNs registration explicitly, and double-forwarding causes duplicate FCM registrations.remote-notificationbackground mode lets iOS wake the app for background/silent orchestration pushes (e.g.trackNotificationflows).
Then add the push entitlement to the app's .entitlements file (create one via Signing & Capabilities → + Capability → Push Notifications if you don't already have it):
<key>aps-environment</key>
<string>development</string>
Use production for release builds. Without this entitlement registerForRemoteNotifications silently fails and you will never receive an APNs token.
- Configure rich push content
- SDK 1.4.x+
- SDK ≤1.2.x
DashX 1.3.0+ delivers pushes as user-visible APNs alert pushes (iOS renders the banner itself). To attach images, register dynamic action buttons, and track delivery when the app isn't running, add a Notification Service Extension target:
- In Xcode, File → New → Target… → Notification Service Extension.
- Delete the auto-generated
NotificationService.swift. - In File → Add Package Dependencies…, add the
DashXNotificationServiceExtensionlibrary to the new NSE target (or addpod 'DashX/NotificationServiceExtension'inside the NSE target in your Podfile). - Create a new
NotificationService.swiftinside the NSE target:
import DashXNotificationServiceExtension
final class NotificationService: DashXNotificationService {}
- Add the same Info.plist keys to the NSE target that your main app uses for xcconfig-driven configuration (same names, same values). The NSE runs in its own process and cannot read the host app's bundle, so it needs its own copies:
<key>DASHX_BASE_URI</key>
<string>$(DASHX_BASE_URI)</string>
<key>DASHX_PUBLIC_KEY</key>
<string>$(DASHX_PUBLIC_KEY)</string>
<key>DASHX_TARGET_ENVIRONMENT</key>
<string>$(DASHX_TARGET_ENVIRONMENT)</string>
DASHX_TARGET_ENVIRONMENT is optional — include it only if your workspace uses environment-scoped broadcasts.
See iOS SDK → Notification Service Extension for a deeper walkthrough.
In 1.2.x, DashX pushes arrive as silent content-available notifications that the main SDK reconstructs into local notifications on the device — no additional Xcode target is required. Rich content (image, action buttons) is built on-device at delivery time.
Silent-push throttling on iOS 18.5 can cause notifications to arrive only when the user opens the app, or not at all. This is fixed in SDK 1.3.0+ — consider upgrading.
The SDK declares Firebase as an optional dependency, so you must add it to your app's build.gradle:
dependencies {
implementation platform('com.google.firebase:firebase-bom:33.8.0')
implementation 'com.google.firebase:firebase-messaging-ktx'
}
You also need a google-services.json from the Firebase Console placed at app/google-services.json, and the Google Services plugin applied in your build files.
The SDK automatically registers DashXFirebaseMessagingService via manifest merger — no manual manifest changes are needed. It handles rendering notifications and tracking delivered, opened, and dismissed events.
On Android 13+, you must request the runtime notification permission (POST_NOTIFICATIONS) before notifications can be shown. See Device Management for a full example.
Steps for iOS
-
Add your iOS app on Firebase Console:
Project Overview > Add App > iOS -
Download
GoogleService-Info.plist -
Add
GoogleService-Info.plist(Add Files…, Copy items if needed). Select the file → File Inspector → Target Membership → check your main app target only (see the iOS tab above). -
In your
Podfileadd the DashX iOS SDK:
pod 'DashX/SDK', :git => 'https://github.com/dashxhq/dashx-ios.git', :tag => '1.5.0'
@dashx/react-native pulls in FirebaseMessaging for you — you don't need to declare it in your Podfile. Make sure your Podfile enables use_modular_headers! (or pins pod 'FirebaseMessaging', :modular_headers => true) so the Swift import resolves.
- Install pods:
cd ios && pod install
- Wire the push callbacks from your app's
AppDelegateinto theDashXNotificationHandlerstatic helpers. The React Native bridge does not ship anAppDelegatebase class — call into the bridge directly from the AppDelegate you already have:
import DashXReactNative
import Firebase
import FirebaseMessaging
import UserNotifications
@main
class AppDelegate: RCTAppDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
Messaging.messaging().delegate = self
UNUserNotificationCenter.current().delegate = self
application.registerForRemoteNotifications()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
DashXNotificationHandler.handleDeviceToken(deviceToken)
}
override func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
DashXNotificationHandler.handleRemoteNotification(
userInfo: userInfo,
completionHandler: completionHandler
)
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
DashXNotificationHandler.handleForegroundNotification(notification, completionHandler: completionHandler)
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
// Fires `messageReceived` + `notificationClicked` to JS and returns the resolved
// URL / NavigationAction / actionIdentifier if your AppDelegate needs them.
_ = DashXNotificationHandler.handleNotificationResponse(response)
completionHandler()
}
}
- Handle tap events from JavaScript:
import DashX from '@dashx/react-native';
const sub = DashX.onNotificationClicked(({ notification, action, actionIdentifier }) => {
// `action` is a resolved NavigationAction: deepLink | screen | richLanding | clickAction
// or `null` if the payload has no navigation intent.
if (action?.type === 'deepLink') {
/* route `action.url` inside your app */
}
});
// sub.remove() on cleanup.
- SDK 1.4.x+
- SDK ≤1.2.x
NSE setup for React Native apps is the same as the native-iOS flow — see iOS SDK → Notification Service Extension. Inside the NSE, subclass DashXNotificationService exactly as a native-iOS integrator would. The RN bridge does not need any extra wiring for the NSE.
In 1.2.x the RN bridge reconstructs the notification on-device from a silent content-available payload — no NSE target is required. Rich content and action buttons are built by DashXNotificationHandler.handleRemoteNotification at delivery time.
Silent-push throttling on iOS 18.5 can cause notifications to arrive only when the user opens the app, or not at all. This is fixed in SDK 1.3.0+ — consider bumping pod 'DashX/SDK' to 1.5.0 in your Podfile.
Steps for Android
DashX requires Google Services installed in your app for Firebase to work:
- Add
google-servicesplugin in your/android/build.gradle
buildscript {
dependencies {
// ... other dependencies
classpath 'com.google.gms:google-services:4.4.2'
}
}
- Add this line in your
/android/app/build.gradle
apply plugin: 'com.google.gms.google-services'
-
Add your Android app on Firebase Console:
Project Overview > Add App > Android -
Download
google-services.jsonfrom there. -
Add
google-services.jsonat the following location:/android/app/google-services.json