iOS - Push Notifications
Overview
Push notifications are a separate entry point from inbound URL deeplinks.
On iOS, Q2MobileApp does not automatically convert a generic push payload into a
built-in FI app deeplink. Instead, push notifications are delivered through app and module
lifecycle handlers. A module can inspect the payload, decide whether the notification
belongs to it, and optionally navigate to a route by calling
moduleDelegate.navigateTo(target: .route(route)).
For built-in Custom URL Scheme deeplinks, built-in Universal Link deeplinks, and canonical route semantics, see Deep Linking.
When This Use Case Applies
Use this pattern when:
- your module owns a push payload format
- your module needs to respond to remote notifications
- tapping the notification should navigate somewhere inside
Q2MobileApp - navigation should be decided from module-specific payload data
Do not use this page as the contract for inbound URL deeplinks. Push notification
handling and URL deeplink intake are different entry points in Q2MobileApp.
How Push Is Handled
For push notifications, current public behavior is:
- silent/background remote notifications are forwarded through the push lifecycle
- user interaction with a delivered notification is forwarded through the push lifecycle
- some built-in app notification flows may be handled by
Q2MobileApp - module-owned push payloads are handed to modules
That means a generic push payload containing something like route: "messages" will not
cause automatic navigation unless a module reads that payload and explicitly calls
moduleDelegate.navigateTo.
Silent Remote Notification
Q2MobileApp receives application(_:didReceiveRemoteNotification:) and forwards the
payload to push modules.
Use this stage when the notification is intended for background processing instead of user-tap navigation.
User Taps The Notification
This is the main stage for route navigation from push.
When the user taps the notification, or taps one of its actions, Q2MobileApp receives
userNotificationCenter(_:didReceive:) and forwards the UNNotificationResponse to
modules.
Notification Arrives In Foreground
When the app is open, Q2MobileApp calls userNotificationCenter(_:willPresent:) and
modules can influence how the notification is presented.
This stage is about presentation behavior, not route navigation after a tap.
Targeting The Correct Module
On Android, modules decide whether a notification belongs to them. The iOS equivalent is explicit sender-based targeting.
For new implementations, use explicit module targeting. Implement sender and include
the matching sender value in the push payload.
PushLifecycleModule inherits PushSenderProvider, so your module can declare:
public final class Q2DemoModule: Q2ModuleBase {
public override class var sender: PushSender? {
PushSender(id: "com.q2.mobile.module.demo")
}
}
At delivery time, Q2MobileApp uses that sender identifier to match the notification to
the correct module. The sender value should be present in the notification userInfo
under PushSender.userInfoKey.
For deterministic routing, the payload sender must exactly match the module's declared
PushSender ID.
Legacy fallback behavior may still exist when sender-based targeting is omitted, but it should not be relied on as the integration contract for targeted delivery. For deeper protocol details and version notes, see Push Lifecycle Module.
Route Navigation From Push
After a module parses a push payload, route navigation is one common next step.
When the module calls:
_ = moduleDelegate.navigateTo(target: .route(route))
Q2MobileApp attempts to navigate that route and then:
- tries to resolve the route as a native module route first
- if no native module handles it, falls back to loading the corresponding UUX route
If the route requires authentication and the user is not authenticated, Q2MobileApp may
defer execution until after login and landing page load.
For canonical route definitions and examples of how routes map from uux.aspx#/..., see
Deep Linking.
Implementation
Once your class inherits from Q2ModuleBase, declare sender, ensure the payload
includes the matching sender value, and override userNotificationCenter(_:didReceive:).
This method receives the push payload and allows you to parse the payload and invoke route
navigation when appropriate.
In the following example:
senderdeclares the module's explicit push target identifierDemoNotificationPayloadmodels the module-specific data needed for navigation- the sample assumes explicit module targeting
Example Code
public class Q2DemoModule: Q2ModuleBase {
public override class var sender: PushSender? {
PushSender(id: "com.q2.mobile.module.demo")
}
public override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
/** Update your service with the new token when this is triggered **/
}
public override func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
guard let moduleDelegate = moduleDelegate else {
return
}
guard let payload = DemoNotificationPayload.make(from: response) else {
return
}
_ = moduleDelegate.navigateTo(target: .route(payload.route))
}
}
private struct DemoNotificationPayload: Decodable {
let route: String
static func make(from response: UNNotificationResponse) -> Self? {
let userInfo = response.notification.request.content.userInfo
guard
let data = userInfo["data"] as? [String: Any],
let demo = data["demo"] as? [String: Any]
else {
return nil
}
do {
let jsonData = try JSONSerialization.data(withJSONObject: demo, options: [])
return try JSONDecoder().decode(Self.self, from: jsonData)
} catch {
print("Error decoding push payload: \(error)")
return nil
}
}
}
Notification Payload Format
For explicit module targeting, include the sender field and ensure it exactly matches
the module's declared PushSender ID. Add a data node alongside the aps section for
module-specific payload content you wish to parse in
userNotificationCenter(_:didReceive:).
{
"aps": {
"alert": {
"title": "title",
"subtitle": "subtitle",
"body": "body"
}
},
"sender": "com.q2.mobile.module.demo",
"data": {
"demo": {
"route": "messages"
}
}
}
What The Module Developer Must Do
The module developer is responsible for:
- declaring a
senderfor explicit module targeting - deciding whether the push belongs to the module
- parsing the module-specific payload
- extracting the route or other navigation target
- calling
moduleDelegate.navigateTo(...)explicitly when navigation is desired
If the module does not call navigateTo, Q2MobileApp will not automatically navigate
for generic push payloads.
Expected Behavior
If the user taps a push notification and your module parses a valid route and calls
moduleDelegate.navigateTo(target: .route(route)), Q2MobileApp will attempt to
navigate to that route.
If the route is invalid or not handled by either native route resolution or UUX navigation, the app remains on the current screen.
Example Routes
| Description | Route |
|---|---|
| Landing Page | landingPage |
| Fund Transfer | transfer |
| Online Activity | transactions/activityCenter |
| Conversations | messages |
| Branches | branches |
| Profile | settings/profile |
These are example routes only. The canonical route explanation lives in Deep Linking.
Summary
This use case is best described as:
- push notification received by
Q2MobileApp - notification handed to app and module lifecycle handlers
- module determines whether the payload belongs to it
- module parses payload and optionally chooses a route
- module asks
Q2MobileAppto navigate Q2MobileAppresolves that route to native UI or UUX