App push on iOS
activeThis 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
}
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 reportsdeliveredorclicked. An app that has never used local notifications has no delegate set at all.Messaging.messaging().delegate— without itdidReceiveRegistrationToken(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.sharedAll 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)
}
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
- Your next flushed batch carries a
bb_notification_registeredevent with the token. - Send a test campaign from the panel. The notification appears, and
bb_notification_deliveredfollows; tapping it producesbb_notification_clicked. Keep the app in the foreground for this check — on iOSdeliveredis reported from the foreground-presentation callback.
See Push events for the full list.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Nothing is displayed | Background Modes → Remote notifications is off, so iOS never wakes the app for a data-only message. |
| Messages arrive but nothing shows | onApplicationDidReceiveRemoteNotification is not being called, or it runs before initializeNotifications. |
didReceiveRegistrationToken never fires | registerForRemoteNotifications() was never called, or Messaging.messaging().delegate was never set — see step 2. Granting permission alone does not issue a token. |
| The token never registers | You 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 events | UNUserNotificationCenter.current().delegate was never set, so the step-4 callbacks never run. |
No delivered events, but taps work | onWillPresentForwarded is not wired, or the app was backgrounded — delivered is reported on foreground presentation. |
| Nothing works on the simulator | Expected — push needs an APNs token, which the simulator does not provide. Test on a physical device. |
| Taps do nothing | Expected — the SDK does not open URLs on iOS. Route interaction.uri in your handler. |
| Your own pushes get displayed twice | Your delegate's else arm is also running for Binoban messages. Branch on source. |
Next steps
- App push on Android · App push in React Native
- iOS SDK reference — the full method surface.
- Push events — what gets emitted, and the payload keys.
- Web push — the browser equivalent.