Push Notifications
This guide will help you to properly configure Push notifications in Apphud.
Integrating Push notifications in your app lets you use Rules — a powerful feature that helps increase revenue by automatically offering a discount (or showing a screen) to a user at the right moment.
Supported platforms: iOS, Android, Flutter, and React NativeRule push notifications are supported on:
- iOS — through APNs
- Android — through FCM, Android SDK 3.4.0 or later
- Flutter — SDK 3.3.0 or later
- React Native — with its native Android dependency on Android SDK 3.4.0 or later (iOS through APNs)
Required Steps
- Generate an APNs authentication key for iOS or a Firebase service account for Android.
- Upload the credentials to Apphud.
- Submit the device push token on iOS and Android using the corresponding SDK methods.
- Handle incoming push notification payloads on iOS and Android using the corresponding SDK methods.
iOS – Generate Push Notifications Auth Key
Go to the Apple Developer Center, open the "Keys" page, then create a new key by entering a name and enabling "Apple Push Notifications service (APNs)":
Once created, download the .p8 file and store it safely — you will upload it to Apphud. Also copy your Team ID from the Developer Account Membership page.
NoteAuth Key file name has the format:
AuthKey_[KEY_ID].p8, whereKEY_IDis your Key Identifier. With an auth key, Apphud can send push notifications to all of your apps in the same Apple Developer account, for both sandbox and production. You can reuse the same auth key for your other apps.
Upload Auth Key to Apphud
Go to the Apphud Dashboard → App settings → Push notifications (iOS / APNs section).
- Upload your APNs Auth Key (
.p8) file. - Enter your Team ID.
- Save settings.
Set up Push Notifications in the iOS app
Add Push Notifications Capability
Make sure Push Notifications is enabled in the Capabilities section of your app target:
Register for Notifications
In your AppDelegate, register for notifications:
import UserNotifications
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Apphud.start(apiKey: YOUR_API_KEY)
registerForNotifications()
// ... the rest of your code
return true
}
func registerForNotifications() {
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
// handle if needed
}
UIApplication.shared.registerForRemoteNotifications()
}Pass Device Token to Apphud
Submit the APNs device token to Apphud:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Apphud.submitPushNotificationsToken(token: deviceToken, callback: nil)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
// Error occurred. Check signing / Push Notifications capability.
}Handle Incoming Push Payload
Handle the payload both when the app is in the foreground and when the user opens a notification:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
if Apphud.handlePushNotification(apsInfo: response.notification.request.content.userInfo) {
// Handled by Apphud — usually nothing else to do
} else {
// Handle other push notifications
}
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
if Apphud.handlePushNotification(apsInfo: notification.request.content.userInfo) {
// Handled by Apphud — usually nothing else to do
} else {
// Handle other push notifications
}
completionHandler([]) // empty array skips showing a banner
}Apphud.handlePushNotification(apsInfo:) returns true if the payload was an Apphud Rule push and was handled by the SDK.
Run the app on a real device and confirm the push token appears on the User page in Apphud.
Android
Android Rules pushes are delivered via Firebase Cloud Messaging (FCM). You need to:
- Create a Firebase project and add your Android app
- Generate a Firebase Service Account JSON key and upload it to Apphud
- Add
google-services.jsonand Firebase Messaging to your Android project - Submit the FCM token to Apphud
- Handle incoming push payloads and provide an Activity for Rule screens
Generate Firebase Service Account for FCM
Apphud sends Android pushes using the FCM HTTP v1 API, which requires a Firebase / Google Cloud Service Account JSON key.
- Open the Firebase Console and select your project (or create one).
- Click the gear icon → Project settings.
- Open the Service accounts tab.
- Click Generate new private key (or follow the link to manage service accounts in Google Cloud).
- Confirm and download the JSON key file. Store it safely — you will upload it to Apphud.
Service Account vs Google Play credentialsThis Firebase Service Account JSON is used only for FCM push delivery. It is separate from the Google Play Service Account JSON used for billing validation in Apphud. Do not reuse one for the other unless you intentionally configured a shared account with both sets of permissions.
Make sure Cloud Messaging is enabled for the project (Firebase Console → Build → Cloud Messaging, or via Google Cloud APIs).
Upload Firebase Service Account to Apphud
Go to the Apphud Dashboard → App settings → Push notifications (Android / FCM section).
- Upload the Firebase Service Account JSON key.
- Save settings.
Set up Push Notifications in the Android app
1. Add your Android app to Firebase and download google-services.json
google-services.json- In Firebase Console → Project settings → Your apps, add an Android app with your package name.
- Download
google-services.json. - Place it in your app module root (typically
app/google-services.json).
2. Add Google Services plugin and Firebase Messaging
In the project-level build.gradle / settings.gradle, apply the Google Services plugin (versions may vary):
// project-level
plugins {
id 'com.google.gms.google-services' version '4.4.2' apply false
}In the app-level build.gradle:
plugins {
id 'com.android.application'
id 'com.google.gms.google-services'
}
dependencies {
implementation platform('com.google.firebase:firebase-bom:33.5.1')
implementation 'com.google.firebase:firebase-messaging-ktx'
}3. Request notification permission (Android 13+)
For regular (visible) pushes on Android 13 (API 33)+, request POST_NOTIFICATIONS at runtime. Silent / data-only Rule pushes may still be delivered without showing a system notification, but requesting permission is recommended if you send regular alerts.
4. Create a FirebaseMessagingService
FirebaseMessagingServiceCreate a service that:
- Submits new FCM tokens to Apphud via
Apphud.submitPushNotificationsToken - Forwards incoming message data to
Apphud.handlePushNotification
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
Apphud.submitPushNotificationsToken(token) { success ->
// optional: log success / failure
}
}
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
val handledByApphud = Apphud.handlePushNotification(message.data)
if (!handledByApphud) {
// Not an Apphud Rule push — handle your own notifications here
}
}
}Register the service in AndroidManifest.xml:
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>5. Submit the current FCM token after SDK start
onNewToken is not always called on every launch. After Apphud.start(...), also fetch the current token and submit it:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Apphud.start(
context = this,
apiKey = "YOUR_API_KEY",
ruleCallback = ruleCallback,
)
FirebaseMessaging.getInstance().token
.addOnCompleteListener { task ->
if (!task.isSuccessful) return@addOnCompleteListener
val token = task.result ?: return@addOnCompleteListener
Apphud.submitPushNotificationsToken(token)
}
}
}6. Provide an Activity for Rule screens (ApphudRuleCallback)
ApphudRuleCallback)When a Rule push is handled (or a pending Rule screen should be shown), the Android SDK needs an Activity to present the in-app screen. Pass an ApphudRuleCallback into Apphud.start and implement provideActivity().
Track the currently resumed Activity and return it from the callback:
class MyApplication : Application() {
@Volatile
private var currentActivity: Activity? = null
private val ruleCallback = object : ApphudRuleCallback {
override fun provideActivity(): Activity? = currentActivity
override fun shouldPerformRule(rule: Rule): Boolean = true
override fun shouldShowScreen(rule: Rule): Boolean = true
// Optional: purchase / survey / dismiss hooks
// override fun onPurchaseCompleted(rule: Rule, result: ApphudPurchaseResult) { ... }
// override fun onRulePaywallWithoutScreen(rule: Rule, paywall: ApphudPaywall) { ... }
}
override fun onCreate() {
super.onCreate()
trackCurrentActivity()
Apphud.start(
context = this,
apiKey = "YOUR_API_KEY",
ruleCallback = ruleCallback,
)
}
private fun trackCurrentActivity() {
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityResumed(activity: Activity) {
currentActivity = activity
}
override fun onActivityPaused(activity: Activity) {
if (currentActivity === activity) currentActivity = null
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = Unit
override fun onActivityStarted(activity: Activity) = Unit
override fun onActivityStopped(activity: Activity) = Unit
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit
override fun onActivityDestroyed(activity: Activity) = Unit
})
}
}
WhyprovideActivity()mattersIf
provideActivity()returnsnull, the SDK falls back to launching the Rule screen in a new task using the application context. Returning the currently visible Activity presents the screen on top of your UI, which is the recommended approach.
That’s it for Android. Run the app, confirm the Push Token is populated on the User page in Apphud, then test a Rule.
Flutter
Flutter SDK 3.3.0 or later supports Rule pushes on both platforms. The dashboard setup is identical: upload the APNs Auth Key + Team ID for iOS and the Firebase Service Account JSON for Android as described above.
Two Dart methods are used, both returning bool:
// iOS: APNs device token as a hex string. Android: FCM registration token.
await Apphud.submitPushNotificationsToken(token);
// iOS: notification `userInfo`. Android: FCM `message.data` (must contain `rule_id`).
// Returns true when Apphud handled the payload as a Rule notification.
await Apphud.handlePushNotification(data);
NoApphudRuleCallbackneeded on FlutterThe Flutter plugin registers the native rule callback for you and supplies the current Activity on Android, so Rule screens are presented automatically. You do not need to implement
provideActivity(). RegisterApphud.setRuleListener(...)only if you want to observe rule events (appear, purchase, dismiss, survey) — see Rules.
iOS in a Flutter app
Handle Apphud pushes natively in ios/Runner/AppDelegate.swift. Apphud delivers iOS pushes directly through APNs (not through FCM), so Dart-side push plugins do not reliably surface them.
Keep your AppDelegate subclassing FlutterAppDelegate and call super in the methods you override:
import Flutter
import UIKit
import UserNotifications
import ApphudSDK
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
Apphud.submitPushNotificationsToken(token: deviceToken, callback: nil)
super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
override func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
Task { @MainActor in
if Apphud.handlePushNotification(apsInfo: response.notification.request.content.userInfo) {
completionHandler()
} else {
super.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
}
}
}
override func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
Task { @MainActor in
if Apphud.handlePushNotification(apsInfo: notification.request.content.userInfo) {
completionHandler([])
} else {
super.userNotificationCenter(center, willPresent: notification, withCompletionHandler: completionHandler)
}
}
}
}Because Apphud.start() is called from Dart, the APNs token often arrives before the SDK is initialized. Keep the token in your AppDelegate and re-submit it once Apphud.start() completes — the example app does this over a small apphud_example/push method channel.
If you prefer to submit the token from Dart, pass the APNs token as a hex string (for example FirebaseMessaging.instance.getAPNSToken()), not the FCM token:
final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (apnsToken != null) {
await Apphud.submitPushNotificationsToken(apnsToken);
}Android in a Flutter app
Complete the Android setup above (google-services.json, Google Services plugin, Firebase Messaging dependency) inside example/android / your android folder, then pick one of two approaches.
Option A — native FirebaseMessagingService (recommended)
FirebaseMessagingService (recommended)Add the service under android/app/src/main/kotlin/.... com.apphud.sdk.Apphud is available without extra dependencies, because the Flutter plugin exposes the Android SDK transitively:
package com.example.myapp
import com.apphud.sdk.Apphud
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
Apphud.submitPushNotificationsToken(token) { success ->
// optional: log success / failure
}
}
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
val handledByApphud = Apphud.handlePushNotification(HashMap<String, Any>(message.data))
if (!handledByApphud) {
// Not an Apphud Rule push — handle your own notifications here
}
}
}Register it in android/app/src/main/AndroidManifest.xml inside <application>:
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>To submit the current token after Apphud.start(), expose a small method channel from MainActivity:
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "myapp/fcm")
.setMethodCallHandler { call, result ->
if (call.method == "submitCurrentToken") {
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
val token = task.result
if (!task.isSuccessful || token == null) {
result.success(false)
} else {
Apphud.submitPushNotificationsToken(token) { success -> result.success(success) }
}
}
} else {
result.notImplemented()
}
}
}
}Option B — forward from Dart with firebase_messaging
firebase_messagingIf your app already uses the firebase_messaging plugin, do not register a second FirebaseMessagingService — FCM delivers a message to only one service. Forward the token and data payload from Dart instead:
import 'package:apphud/apphud.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
Future<void> setupApphudPush() async {
await FirebaseMessaging.instance.requestPermission();
// Submit the current token after Apphud.start() and on every refresh.
final token = await FirebaseMessaging.instance.getToken();
if (token != null) {
await Apphud.submitPushNotificationsToken(token);
}
FirebaseMessaging.instance.onTokenRefresh.listen(Apphud.submitPushNotificationsToken);
// Forward incoming Rule payloads. Apphud Rule pushes contain `rule_id`.
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
final handledByApphud = await Apphud.handlePushNotification(message.data);
if (!handledByApphud) {
// Not an Apphud Rule push — handle your own notification here
}
});
}
Background messages
FirebaseMessaging.onBackgroundMessageruns in a separate Dart isolate where the Apphud plugin is not initialized. Handle Apphud Rule payloads either fromonMessage(foreground) or from a nativeFirebaseMessagingService(Option A), which works in all app states.
Dart usage summary
import 'package:apphud/apphud.dart';
await Apphud.start(apiKey: 'YOUR_API_KEY');
// Optional: observe rule lifecycle events. Screens are presented automatically.
await Apphud.setRuleListener(listener: myRuleListener);
// Submit the push token (see platform sections above for how to obtain it).
await Apphud.submitPushNotificationsToken(token);
// Optional helpers
await Apphud.checkRules(); // poll for unread rules right now
final rule = await Apphud.pendingRule(); // pending / displayed rule metadata
await Apphud.showPendingRuleScreen(); // present a delayed rule screenReact Native
The Apphud React Native SDK wraps the native iOS and Android SDKs, so Rule pushes work once the native setup is in place — complete the iOS (APNs) and Android (FCM, Android SDK 3.4.0 or later) steps above for the native side of your app.
Get the device push token from your app's usual React Native push setup — the APNs token on iOS, the FCM registration token on Android — then pass it to Apphud and forward incoming Rule payloads:
import { ApphudSdk } from '@apphud/react-native-apphud-sdk';
// iOS: APNs device token (hex string). Android: FCM registration token.
ApphudSdk.submitPushNotificationsToken(token);
// Forward incoming Rule payloads. Apphud Rule pushes contain `rule_id`.
// iOS: the notification userInfo. Android: the full FCM `message.data` map.
ApphudSdk.handlePushNotification(payload);See Push Notification Payload for the fields Apphud sends in a Rule push.
Push Notification Payload
Example payload (fields used by Apphud Rules):
{
"aps": {
"alert": {
"title": "Paywall!",
"body": "TEST"
},
"mutable-content": 1,
"sound": "default"
},
"custom": {
"a": {}
},
"rule_id": "5242761d-918d-42ae-a49b-f155b1402c2b",
"rule_name": "Paywall Custom Rule",
"screen_id": "0f8b832e-65d3-4777-a303-ba89750763c9",
"screen_name": "your_screen_name",
"paywall_id": "0f8b832e-65d3-4777-a303-ba89750763c9",
"paywall_identifier": "your_screen_name",
}On Android, Apphud Rule data arrives in RemoteMessage.data (key/value map). Pass that map to Apphud.handlePushNotification(data). On Flutter the same map is passed as Map<String, dynamic>.
Silent Push Notifications
Apphud supports silent push notifications delivered in the background without alerting the user.
Silent pushes:
- do not require user permission (on iOS) and are designed to be lightweight;
- do not show alerts, sounds, or banners.
Typical uses:
- Background data updates (e.g. refreshing subscription status)
- Quiet in-app logic (preparing personalized content)
- Updating badge / local storage
- Supporting Rules automations without interrupting the user
See Rules → Push notification type for details.
Testing Rule push notifications
iOS
Test on a real device (Xcode or TestFlight). The iOS Simulator does not deliver real APNs pushes.
Before testing:
- APNs Auth Key is uploaded in Apphud and Team ID is set.
- The test user has a valid Push Token on the User page.
- Push notifications are allowed on the device.
- The app calls
Apphud.submitPushNotificationsToken(token:deviceToken, callback: nil). - The app calls
Apphud.handlePushNotification(apsInfo: userInfo)for incoming payloads.
Android
Test on a real device or emulator with Google Play Services.
Before testing:
- Firebase Service Account JSON is uploaded in Apphud.
google-services.jsonmatches the app package name.- The test user has a valid Push Token on the User page.
- The app calls
Apphud.submitPushNotificationsToken(token). - The app calls
Apphud.handlePushNotification(message.data)from yourFirebaseMessagingService. ApphudRuleCallback.provideActivity()returns the current Activity when a screen should be shown.
Flutter
- Use Flutter SDK 3.3.0 or later, and complete the native setup for the platform you are testing.
- iOS: test on a real device, and confirm the APNs token is submitted after
Apphud.start()(not only when it first arrives). - Android: confirm your
FirebaseMessagingServiceis the one receiving messages, or that Dart forwarding is wired for foreground messages. - The test user has a valid Push Token on the User page.
- No
ApphudRuleCallbackwork is required — Rule screens are presented by the plugin.
React Native
- Complete the native setup for the platform you're testing — iOS (APNs) and/or Android (FCM, Android SDK 3.4.0 or later).
- iOS: test on a real device, and confirm the APNs token is submitted with
submitPushNotificationsTokenafterApphud.start(). - Android: make sure only one
FirebaseMessagingServicereceives the message, and forward the fulldatamap tohandlePushNotification. - Confirm the Push Token is populated on the User page in Apphud, then run a Rule test below.
Run a test (all platforms)
- Open the Rule → Actions → Test → enter the test User ID.
- Wait a few seconds, then refresh the User page in Apphud.
- Confirm Last push sent at updated.
- Open / bring the app to foreground on the test device — the Rule’s in-app screen should present if one is configured.
Troubleshooting
Push notification doesn't arrive
iOS
- Confirm Auth Key, Key ID, and Team ID are set in Apphud.
- Confirm
Apphud.submitPushNotificationsTokenis called with a non-empty token. - Open the User page — Push Token must be populated.
Android
- Confirm the Firebase Service Account JSON is uploaded in Apphud Push notifications settings.
- Confirm
google-services.jsonis present and package name matches. - Confirm FCM token is submitted via
Apphud.submitPushNotificationsToken. - Open the User page — Push Token must be populated.
- Confirm Google Play Services are available on the device / emulator.
Flutter
- Confirm the push token is submitted after
Apphud.start()completed — a token submitted before initialization is not stored. - iOS: confirm you submit the APNs token (hex string or
Data), not an FCM token. - Android: confirm only one
FirebaseMessagingServiceis declared in the merged manifest.
Push arrives but is not handled
iOS
- Call
Apphud.handlePushNotification(apsInfo: userInfo). - Ensure
UNUserNotificationCenterDelegateis set. - If using OneSignal or a
UNNotificationServiceExtension, verify Apphud still receives the payload.
Android
- Call
Apphud.handlePushNotification(message.data)insideonMessageReceived. - Pass the data map (
RemoteMessage.data), not only notification title/body. - Ensure
Apphud.start(...)has already been called before handling the push. - Implement
ApphudRuleCallback.provideActivity()so Rule screens can be presented.
Flutter
- iOS: handle the payload in
AppDelegate— Apphud pushes come straight from APNs and are not routed through Dart push plugins. - iOS: make sure your
AppDelegatesubclassesFlutterAppDelegateand callssuperin overridden lifecycle methods. - Android:
FirebaseMessaging.onBackgroundMessageruns in an isolate without the plugin — use a native service instead. - The Apphud Rule payload must contain
rule_id; logmessage.datato verify.
Push tokens are submitted, but users don't receive Rule pushes
- Log around token submission and confirm the token is not empty / null.
- Confirm Push Token on the User page in Apphud.
- Test with the same user / device shown in Apphud.
- Confirm notification permission where required (iOS; Android 13+ for visible alerts).
- Confirm the Rule’s target audience users actually have push tokens.
If the issue reproduces for a specific user, send Apphud Support the User URL / User ID and the Rule URL.
Using both OneSignal and Apphud Push Notifications
- iOS: see this guide.
- Android: if another push SDK also implements
FirebaseMessagingService, make sure Apphud still receives FCM tokens and data payloads (either by forwarding from that SDK, or by ensuring your Apphud-aware service is reached). Always call:Apphud.submitPushNotificationsToken(token)Apphud.handlePushNotification(message.data)for Apphud Rule payloads
- Flutter: the same rule applies — only one
FirebaseMessagingServicereceives each FCM message, so forward tokens and Rule payloads from whichever service or Dart handler your app actually uses.
Updated 1 day ago
