ChangelogEdit pageSTAFFOpen dashboard

Push notifications

Connect Firebase for Android and your APNs key for iOS. Bubbl receives its own pushes and draws them, so there's no push code to write.

Bubbl sends two kinds of push, and your app doesn't forward either of them:

  • Android: through Firebase Cloud Messaging, using your Firebase project. Bubbl's pushes are data only: the SDK receives each one with a receiver of its own, beside any FirebaseMessagingService your app has, and draws the notification itself.
  • iOS: straight from Bubbl to Apple (APNs) with your app's auth key, without Firebase. iOS draws the alert without running your app; tapping it opens Bubbl's notification screen.

What the user sees and how a notification opens is the same however it arrived: see Notifications and surveys.

Android push#

  1. 1
    Upload your Firebase service account
    In the Firebase console, create a service-account key for your project. In the dashboard, open Configuration › Firebase and, under Credentials, choose the service-account JSON and Validate and upload. Bubbl sends your Android pushes with it.
  2. 2
    Give the SDK your Firebase app
    Either keep google-services.json in your app with the com.google.gms.google-services Gradle plugin, as usual (the file alone, without the plugin, is ignored), or leave Firebase files out of your app and upload the project's google-services.json under Android apps on the same page, then Check and save. It must include your app's package name; Bubbl then starts Firebase for you.
  3. 3
    Check the token
    Run the app and read hasPushToken in Bubbl.diagnostics(). With the dashboard's google-services.json, Bubbl gets the Firebase settings when it first registers, so the token can come on the second launch or within the hour, not the first.
Use the same Firebase projectWhen your app starts Firebase itself, Bubbl uses your app's Firebase project and ignores any google-services.json in the dashboard. That project must be the one whose service account you uploaded. If it isn't, the phone gets a token but none of Bubbl's pushes, and nothing reports an error.

For Expo, set android.googleServicesFile in app.json to keep the file in your app: prebuild then adds the file and the Gradle plugin.

Your own Firebase messaging#

Keep your own FirebaseMessagingService, firebase_messaging or React Native Firebase. Bubbl receives its pushes beside it and needs no tokens or payloads from you. Bubbl's pushes also reach your handler, so skip them there:

KOTLIN
override fun onMessageReceived(message: RemoteMessage) {
    if (Bubbl.isBubblMessage(message.data)) return
    // your own pushes
}
DART
FirebaseMessaging.onMessage.listen((message) {
  if (Bubbl.isBubblMessage(message.data)) return;
  // your own pushes
});
TS
messaging().onMessage(async (remoteMessage) => {
  if (Bubbl.isBubblMessage(remoteMessage.data)) return;
  // your own pushes
});

Status bar icon and channel#

Bubbl's status bar icon is your app's tech.bubbl.sdk.notification_icon meta-data. Without it, Bubbl uses Firebase's default notification icon (com.google.firebase.messaging.default_notification_icon), then your app icon.

ANDROIDMANIFEST.XML
<application ...>
    <meta-data
        android:name="tech.bubbl.sdk.notification_icon"
        android:resource="@drawable/ic_notification" />
</application>

Bubbl posts its notifications on its own high-importance channel, named "Offers and updates" in the system settings. Rename it with the bubbl_channel_name and bubbl_channel_description string resources: see Appearance and text.

iOS push#

  1. 1
    Add the Push Notifications capability
    In Xcode, add the Push Notifications capability to your app target under Signing & Capabilities (the Runner target in Flutter). It adds the aps-environment entitlement. Expo's config plugin adds the entitlement for you at prebuild.
  2. 2
    Add your app and APNs key in the dashboard
    Create an APNs auth key at developer.apple.com › Keys with Apple Push Notifications enabled. In the dashboard, open Configuration › Apple Push and, under Add an app, enter the Bundle ID, Key ID and Team ID, choose the APNs auth key (.p8), and Check and save. Bubbl checks the key with Apple before saving. Add each iOS app by its bundle ID.
  3. 3
    Let Bubbl get the device token
    Don't call registerForRemoteNotifications() yourself. Bubbl asks iOS for the device token once notifications are allowed: straight after Bubbl.permissions.requestNotifications(), and at every app open, so permission given through your own prompt, another plugin or Settings is picked up too. hasPushToken in diagnostics turns true once Bubbl has it.

Sandbox or production APNs#

The SDK reads your app's provisioning profile to tell Bubbl which APNs environment the token belongs to. A profile with aps-environment set to development (Xcode's development builds) is sandbox. Ad hoc, TestFlight and App Store builds, and builds with no profile, are production. Bubbl sends each push in the environment the phone registered with.

Your own notification handling#

Your own UNUserNotificationCenter delegate, Firebase, or other plugins such as flutter_local_notifications need no changes. Bubbl extends whichever notification delegate is set, answers only for Bubbl's pushes, and passes everything else on unchanged. If no delegate is set by the end of launch, Bubbl sets its own. It adds itself to your app delegate's application(_:didRegisterForRemoteNotificationsWithDeviceToken:) the same way, SwiftUI's UIApplicationDelegateAdaptor included. Bubbl.isBubblMessage(userInfo) tells you whether a payload is Bubbl's.

Handing over pushes yourself#

To turn the automatic integration off, set BubblAutoIntegrationEnabled to NO in Info.plist, then hand Bubbl the device token and the notification delegate's two calls yourself. willPresent and didReceive return true for Bubbl's pushes, and Bubbl then calls the completion handler.

INFO.PLIST
<key>BubblAutoIntegrationEnabled</key>
<false/>
APPDELEGATE.SWIFT
import BubblSDK
import UserNotifications

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Bubbl.setPushToken(deviceToken)   // fine before Bubbl.start too
    // your own token handling
}

func userNotificationCenter(_ center: UNUserNotificationCenter,
                            willPresent notification: UNNotification,
                            withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    if Bubbl.willPresent(notification, completionHandler: completionHandler) { return }
    completionHandler([.banner, .sound])   // yours
}

func userNotificationCenter(_ center: UNUserNotificationCenter,
                            didReceive response: UNNotificationResponse,
                            withCompletionHandler completionHandler: @escaping () -> Void) {
    if Bubbl.didReceive(response, completionHandler: completionHandler) { return }
    completionHandler()
}
APPDELEGATE.SWIFT
import bubbl_flutter_sdk   // gives you the iOS SDK's Bubbl, under CocoaPods and Swift Package Manager alike

// Then call Bubbl.setPushToken, Bubbl.willPresent and Bubbl.didReceive
// from the same three methods as a native iOS app.
APPDELEGATE.MM
#import <UserNotifications/UserNotifications.h>
#import <BubblReactNativeSdk/BubblReactNativeSdk-Swift.h>

// didRegisterForRemoteNotificationsWithDeviceToken:
[BubblPush setPushToken:deviceToken];
// willPresentNotification:withCompletionHandler:
if ([BubblPush willPresent:notification completionHandler:completionHandler]) return;
// didReceiveNotificationResponse:withCompletionHandler:
if ([BubblPush didReceive:response completionHandler:completionHandler]) return;

In a Swift React Native AppDelegate, import BubblReactNativeSdk and call Bubbl.setPushToken, Bubbl.willPresent and Bubbl.didReceive as in the native iOS example. In this mode Bubbl doesn't touch your app delegate or notification delegate, but it still asks iOS for the device token once notifications are allowed. Calling registerForRemoteNotifications() yourself as well does no harm.

Pictures on iOS pushes#

iOS draws Bubbl's pushes without running your app, so a push's picture needs a Notification Service Extension. The extension downloads the push's image_url (https only, at most 10 MB) and attaches it. If anything goes wrong, the push shows without the picture. Other pushes pass through untouched.

  1. In Xcode, choose File › New › Target… › Notification Service Extension, named NotificationService here, with the same deployment target as your app.
  2. Link Bubbl's extension code to that target (see the platform tab below).
  3. Replace the whole of the extension's NotificationService.swift with the two lines below. Don't keep Xcode's template code: it overrides didReceive and serviceExtensionTimeWillExpire without calling super, so Bubbl's code would never run.
XCODE
Add the package's BubblNotificationService product to the NotificationService extension target
(the extension's target › General › Frameworks and Libraries, or when adding the package).
PODFILE
# After the Runner target:
target 'NotificationService' do
  use_frameworks! :linkage => :static
  pod 'BubblFlutterNotificationService', :path => '.symlinks/plugins/bubbl_flutter_sdk/ios'
end
PODFILE
# Outside your app's target, then run pod install:
target 'NotificationService' do
  pod 'BubblReactNativeNotificationService', :path => '../node_modules/@bubblsdk/react-native-sdk/ios-extension'
end

Then make the extension's NotificationService.swift just this, on every platform:

NOTIFICATIONSERVICE.SWIFT
import BubblNotificationService

class NotificationService: BubblNotificationService {}
  • Flutter: the Podfile block works with Swift Package Manager on too, because Flutter installs pods whenever the app has a Podfile. An app on Swift Package Manager with no ios/Podfile creates one first: run flutter config --no-enable-swift-package-manager, flutter build ios once (it writes ios/Podfile), then flutter config --enable-swift-package-manager, and add the block. Then, in Xcode's Runner target › Build Phases, drag "Embed Foundation Extensions" above "Thin Binary", or Xcode reports a dependency cycle.
  • Expo: set "imageNotifications": true under ios in Bubbl's config plugin options. Prebuild adds the extension (bundle ID: yours plus .BubblNotificationExtension), its Podfile target and its EAS Build entry, so EAS signs it. It needs ios.bundleIdentifier.
  • An extension that already does its own work: override didReceive(_:withContentHandler:) and call super only when BubblNotificationService.isBubbl(request) is true.

Send a test push#

  1. Read installId from Bubbl.diagnostics() on your phone.
  2. In the dashboard, open the Active users card on the home page, search for that install ID, and choose Send test push. In a Sandbox, Configuration › Test Devices has Send test push beside each approved phone too.
  3. It arrives as a plain notification that opens your app. It reports no events.

The button is greyed out while the device has no push token. active must be true in diagnostics or the phone drops the push: for example with requireConsent before setConsent(true), or while the server has paused Bubbl. A device that opted out gets no test push.

Configuration › Firebase and Configuration › Apple Push also have Send a test push, which takes a full token directly for a phone that hasn't registered yet. On Apple Push, choose the environment for a full token: Sandbox (Xcode builds) or Production (TestFlight, App Store).

When pushes don't arrive#

  • Android: check that the app's Firebase project is the one whose service account is uploaded, that the package name is in the dashboard's google-services.json, and that the phone wasn't force-stopped (Android wakes no app after a force stop until it's opened again).
  • iOS: check the bundle ID and key under Configuration › Apple Push, and that the build's APNs environment matches (a development build gets sandbox pushes). iOS couldn't give the app a device token in the log usually means the Push Notifications capability or provisioning is missing.

More in Troubleshooting.