Skip to main content
engage

App push on iOS

active
Audience: developerUpdated 2026-08-09

This how-to wires the Binoban Native SDK into your iOS app's Firebase Cloud Messaging setup, so Engage pushes are displayed and their delivery, taps, and dismissals reported.

Work through App push first — it covers the prerequisites, the shape of the integration, and the source marker the examples below branch on.

1. Enable the capabilities

In Xcode, add Push Notifications and Background Modes → Remote notifications. Background Modes is required: Binoban's messages are data-only, and without it iOS will not wake your app to display them.

2. Initialize at launch

Initialize the SDK's notification layer, claim the two delegates the steps below forward from, and register with APNs:

import binoban
import FirebaseCore
import FirebaseMessaging

func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure() // your existing Firebase setup

BinobanNotifications.shared.initializeNotifications(
configuration: NotificationPlatformConfigurationIos(
askNotificationPermissionOnStart: true,
notificationSoundName: nil
)
)

UNUserNotificationCenter.current().delegate = self
Messaging.messaging().delegate = self
application.registerForRemoteNotifications()

return true
}
Those last three lines are not optional

The SDK sets none of them for you, and each one fails silently if you skip it:

  • UNUserNotificationCenter.current().delegate — without it the callbacks in step 4 never fire, so nothing reports delivered or clicked. An app that has never used local notifications has no delegate set at all.
  • Messaging.messaging().delegate — without it didReceiveRegistrationToken (step 3) never reaches your code.
  • registerForRemoteNotifications() — without it iOS never issues an APNs token, so Firebase never mints an FCM token and the device stays unreachable. Permission alone is not enough.

self here is whatever object implements the delegate methods — the examples below assume your AppDelegate does, so declare it as UNUserNotificationCenterDelegate and MessagingDelegate.

BinobanNotifications.shared

All notification entry points live on BinobanNotifications.shared (SDK 1.1.0 and later). They are Kotlin top-level functions underneath, so Swift also sees them on the generated NotificationForwarding_iosKt — that older spelling still works, but prefer the one shown here. Note also that the configuration type is NotificationPlatformConfigurationIos, spelled flat rather than nested.

askNotificationPermissionOnStart: true asks for notification permission as soon as the SDK initializes. Set it to false to ask at a moment of your choosing — usually the better experience.

notificationSoundName takes the name of a sound file in your target's bundle resources, or nil for the system default.

3. Register the Firebase token

The token Binoban needs is the FCM registration token, from Firebase's MessagingDelegate — not the raw APNs device token:

import FirebaseMessaging
import binoban

func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
guard let token = fcmToken else { return }
BinobanNotifications.shared.onNewToken(token: token)
}
Not the APNs token

Do not pass the Data from didRegisterForRemoteNotificationsWithDeviceToken. Binoban sends through Firebase, so an APNs device token cannot be used to reach the device. Hand Firebase the APNs key (see Firebase setup) and give Binoban the FCM token.

The APNs token goes to Firebase. Firebase's method swizzling normally passes it along for you; if you disabled swizzling (FirebaseAppDelegateProxyEnabled = NO), set it yourself:

func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
Messaging.messaging().apnsToken = deviceToken
}

Register the token you already have

didReceiveRegistrationToken only fires when Firebase generates a token — on a fresh install, or on rotation. An app that already uses FCM has a token in hand, and that callback will not fire again for existing installs. Fetch it once at startup so those devices register too:

Messaging.messaging().token { token, _ in
guard let token else { return }
BinobanNotifications.shared.onNewToken(token: token)
}

onNewToken is safe to call repeatedly — registering the same token twice is harmless.

4. Forward the notification callbacks

The SDK never registers its own UNUserNotificationCenterDelegate — your app owns it and forwards to BinobanNotifications.shared.

Incoming data push — this is what displays a Binoban notification. Firebase flattens a data message's keys onto userInfo, so source sits at its top level:

func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
if userInfo["source"] as? String == "binoban" {
BinobanNotifications.shared.onApplicationDidReceiveRemoteNotification(userInfo: userInfo)
} else {
// your own push handling
}
completionHandler(.newData)
}

As on Android, the source branch is for your benefit, not the SDK's: onApplicationDidReceiveRemoteNotification ignores non-Binoban payloads by itself. Call it bare if this delegate only ever sees Binoban messages.

Foreground presentation — reports delivered.

func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
BinobanNotifications.shared.onWillPresentForwarded(userInfo: notification.request.content.userInfo)
completionHandler([.banner, .sound])
}

User interaction — reports clicked, or closed on a swipe-away.

func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let actionId = response.actionIdentifier == UNNotificationDefaultActionIdentifier
? nil : response.actionIdentifier
let dismissed = response.actionIdentifier == UNNotificationDismissActionIdentifier

BinobanNotifications.shared.onDidReceiveForwarded(
userInfo: response.notification.request.content.userInfo,
actionId: actionId,
dismissed: dismissed
)
completionHandler()
}

actionId is nil for a plain body tap and the button's identifier otherwise; dismissed is true only when the user swiped the notification away.

5. Route the tap yourself

The SDK does not open URLs on iOS. iOS already delivers every tap to your delegate, and routing belongs to your navigation. Register a handler to read the target and any custom data:

import binoban

class MyNotificationHandler: DefaultNotificationInteractionHandler {
override func onNotificationInteraction(interaction: NotificationInteraction) {
super.onNotificationInteraction(interaction: interaction) // keep SDK tracking

if let uri = interaction.uri, let url = URL(string: uri) {
UIApplication.shared.open(url) // or route in-app
}
let data = interaction.customData // your push customData, or nil
_ = data
}
}

// once, at startup:
NotificationInteractionManager.shared.setHandler(handler: MyNotificationHandler())

interaction.uri resolves to the tapped button's target when a button was pressed, or the notification's main target for a body tap.

Install the handler as early in launch as you can. On a cold start from a notification tap the interaction can dispatch before your setHandler call runs; if that matters to your routing, drain the buffered ones afterwards with drainPendingInteractions(). They are already tracked, so treat them as delivery-only and do not re-track them.

Verify it worked

  1. Your next flushed batch carries a bb_notification_registered event with the token.
  2. Send a test campaign from the panel. The notification appears, and bb_notification_delivered follows; tapping it produces bb_notification_clicked. Keep the app in the foreground for this check — on iOS delivered is reported from the foreground-presentation callback.

See Push events for the full list.

Troubleshooting

SymptomLikely cause
Nothing is displayedBackground Modes → Remote notifications is off, so iOS never wakes the app for a data-only message.
Messages arrive but nothing showsonApplicationDidReceiveRemoteNotification is not being called, or it runs before initializeNotifications.
didReceiveRegistrationToken never firesregisterForRemoteNotifications() was never called, or Messaging.messaging().delegate was never set — see step 2. Granting permission alone does not issue a token.
The token never registersYou passed the APNs device token instead of the FCM token — or the app already had a token, so the callback never fired again. See Register the token you already have.
No delivered or clicked eventsUNUserNotificationCenter.current().delegate was never set, so the step-4 callbacks never run.
No delivered events, but taps workonWillPresentForwarded is not wired, or the app was backgrounded — delivered is reported on foreground presentation.
Nothing works on the simulatorExpected — push needs an APNs token, which the simulator does not provide. Test on a physical device.
Taps do nothingExpected — the SDK does not open URLs on iOS. Route interaction.uri in your handler.
Your own pushes get displayed twiceYour delegate's else arm is also running for Binoban messages. Branch on source.

Next steps