Comprehensive notification routing system for managing push notifications and local notifications with plugin integration.
Central notification routing system that manages push and local notifications, delegating to appropriate plugin handlers.
/**
* Notification router that manages push and local notifications for Capacitor
*/
@objc(CAPNotificationRouter)
public class NotificationRouter: NSObject, UNUserNotificationCenterDelegate {
// MARK: - Handler Properties
/** Handler for push notifications */
public weak var pushNotificationHandler: NotificationHandlerProtocol?
/** Handler for local notifications */
public weak var localNotificationHandler: NotificationHandlerProtocol?
/** Whether Capacitor should automatically manage UNUserNotificationCenter delegate (computed property) */
public var handleApplicationNotifications: Bool { get set }
// MARK: - UNUserNotificationCenterDelegate Methods
/**
* Called when notification is received while app is in foreground
* Routes to appropriate handler based on notification type
* @param center Notification center
* @param notification Received notification
* @param completionHandler Completion handler for presentation options
*/
public func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
)
/**
* Called when user interacts with notification
* Routes to appropriate handler based on notification type
* @param center Notification center
* @param response User's response to notification
* @param completionHandler Completion handler
*/
public func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
)
}Usage Examples:
// Basic notification router setup
override func instanceDescriptor() -> InstanceDescriptor {
let descriptor = InstanceDescriptor()
// Enable automatic notification handling
descriptor.handleApplicationNotifications = true
return descriptor
}
override func capacitorDidLoad() {
super.capacitorDidLoad()
// Set up notification handlers
if let router = bridge?.notificationRouter {
router.pushNotificationHandler = MyPushNotificationHandler()
router.localNotificationHandler = MyLocalNotificationHandler()
}
}
// Custom notification router setup
class CustomBridgeViewController: CAPBridgeViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Manual notification center delegate setup
UNUserNotificationCenter.current().delegate = self
}
}
extension CustomBridgeViewController: UNUserNotificationCenterDelegate {
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
// Custom handling or delegate to Capacitor router
bridge?.notificationRouter.userNotificationCenter(
center,
willPresent: notification,
withCompletionHandler: completionHandler
)
}
}Protocol that plugins must implement to handle push and local notifications.
/**
* Protocol for handling notifications in plugins
*/
@objc(CAPNotificationHandlerProtocol)
public protocol NotificationHandlerProtocol {
/**
* Called when notification will be presented while app is in foreground
* @param notification The notification to be presented
* @returns Presentation options for the notification
*/
func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions
/**
* Called when user interacts with a notification
* @param response User's response to the notification
*/
func didReceive(response: UNNotificationResponse)
}Implementation Examples:
// Push notification handler
class PushNotificationHandler: NSObject, NotificationHandlerProtocol {
func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions {
// Determine how to present push notifications in foreground
let userInfo = notification.request.content.userInfo
// Check if this is a silent notification
if let aps = userInfo["aps"] as? [String: Any],
let contentAvailable = aps["content-available"] as? Int,
contentAvailable == 1 {
return [] // Silent notification, don't show
}
// Show banner and play sound for regular push notifications
return [.banner, .sound, .badge]
}
func didReceive(response: UNNotificationResponse) {
let userInfo = response.notification.request.content.userInfo
let actionIdentifier = response.actionIdentifier
// Handle different notification actions
switch actionIdentifier {
case UNNotificationDefaultActionIdentifier:
// User tapped the notification
handleNotificationTap(userInfo: userInfo)
case UNNotificationDismissActionIdentifier:
// User dismissed the notification
handleNotificationDismiss(userInfo: userInfo)
default:
// Custom action
handleCustomAction(actionIdentifier, userInfo: userInfo)
}
}
private func handleNotificationTap(userInfo: [AnyHashable: Any]) {
// Navigate to specific screen or trigger action
NotificationCenter.default.post(
name: NSNotification.Name("PushNotificationTapped"),
object: nil,
userInfo: userInfo
)
}
}
// Local notification handler
class LocalNotificationHandler: NSObject, NotificationHandlerProtocol {
func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions {
// Always show local notifications with full presentation
return [.banner, .sound, .badge]
}
func didReceive(response: UNNotificationResponse) {
let identifier = response.notification.request.identifier
let userInfo = response.notification.request.content.userInfo
// Handle local notification response
handleLocalNotificationResponse(
identifier: identifier,
userInfo: userInfo,
actionIdentifier: response.actionIdentifier
)
}
private func handleLocalNotificationResponse(
identifier: String,
userInfo: [AnyHashable: Any],
actionIdentifier: String
) {
// Process local notification interaction
print("Local notification \(identifier) action: \(actionIdentifier)")
}
}How plugins integrate with the notification system for handling notifications.
// Notification-aware plugin
class NotificationPlugin: CAPPlugin, NotificationHandlerProtocol {
override func load() {
super.load()
// Register as notification handler
bridge?.notificationRouter.pushNotificationHandler = self
bridge?.notificationRouter.localNotificationHandler = self
// Request notification permissions
requestNotificationPermissions()
}
// MARK: - Plugin Methods
@objc func requestPermissions(_ call: CAPPluginCall) {
UNUserNotificationCenter.current().requestAuthorization(
options: [.alert, .sound, .badge]
) { granted, error in
DispatchQueue.main.async {
if let error = error {
call.reject("Permission request failed", "PERMISSION_ERROR", error)
} else {
call.resolve(["granted": granted])
}
}
}
}
@objc func scheduleLocal(_ call: CAPPluginCall) {
let title = call.getString("title", "Notification")
let body = call.getString("body", "")
let delay = call.getDouble("delay", 0)
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(
timeInterval: delay,
repeats: false
)
let identifier = UUID().uuidString
let request = UNNotificationRequest(
identifier: identifier,
content: content,
trigger: trigger
)
UNUserNotificationCenter.current().add(request) { error in
DispatchQueue.main.async {
if let error = error {
call.reject("Failed to schedule notification", "SCHEDULE_ERROR", error)
} else {
call.resolve(["identifier": identifier])
}
}
}
}
@objc func registerForPush(_ call: CAPPluginCall) {
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
call.resolve()
}
}
// MARK: - NotificationHandlerProtocol
func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions {
let userInfo = notification.request.content.userInfo
// Notify JavaScript of foreground notification
notifyListeners("notificationReceived", data: [
"title": notification.request.content.title,
"body": notification.request.content.body,
"data": userInfo
])
// Customize presentation based on app state
return [.banner, .sound]
}
func didReceive(response: UNNotificationResponse) {
let notification = response.notification
let actionIdentifier = response.actionIdentifier
// Notify JavaScript of notification interaction
notifyListeners("notificationActionPerformed", data: [
"identifier": notification.request.identifier,
"title": notification.request.content.title,
"body": notification.request.content.body,
"actionId": actionIdentifier,
"data": notification.request.content.userInfo
])
}
// MARK: - Private Methods
private func requestNotificationPermissions() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
if settings.authorizationStatus == .notDetermined {
// Will be requested by JavaScript call
}
}
}
}
// Register the plugin
CAP_PLUGIN(NotificationPlugin, NotificationPlugin,
CAP_PLUGIN_METHOD(requestPermissions, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(scheduleLocal, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(registerForPush, CAPPluginReturnPromise);
)Complex notification handling scenarios and patterns.
Multi-Handler Setup:
class AdvancedNotificationManager {
private var handlers: [NotificationHandlerProtocol] = []
func addHandler(_ handler: NotificationHandlerProtocol) {
handlers.append(handler)
}
func setupRouting(with router: NotificationRouter) {
// Create composite handlers
router.pushNotificationHandler = CompositePushHandler(handlers: handlers)
router.localNotificationHandler = CompositeLocalHandler(handlers: handlers)
}
}
class CompositePushHandler: NSObject, NotificationHandlerProtocol {
private let handlers: [NotificationHandlerProtocol]
init(handlers: [NotificationHandlerProtocol]) {
self.handlers = handlers
}
func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions {
// Combine options from all handlers
var options: UNNotificationPresentationOptions = []
for handler in handlers {
let handlerOptions = handler.willPresent(notification: notification)
options.formUnion(handlerOptions)
}
return options
}
func didReceive(response: UNNotificationResponse) {
// Notify all handlers
for handler in handlers {
handler.didReceive(response: response)
}
}
}Conditional Notification Handling:
class ConditionalNotificationHandler: NSObject, NotificationHandlerProtocol {
func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions {
let userInfo = notification.request.content.userInfo
// Check notification category
if let category = userInfo["category"] as? String {
switch category {
case "urgent":
return [.banner, .sound, .badge]
case "silent":
return []
case "background":
return [.badge]
default:
return [.banner]
}
}
return [.banner, .sound]
}
func didReceive(response: UNNotificationResponse) {
let userInfo = response.notification.request.content.userInfo
// Route based on notification type
if let type = userInfo["type"] as? String {
switch type {
case "chat":
handleChatNotification(response)
case "reminder":
handleReminderNotification(response)
case "update":
handleUpdateNotification(response)
default:
handleGenericNotification(response)
}
}
}
private func handleChatNotification(_ response: UNNotificationResponse) {
// Navigate to chat screen
}
private func handleReminderNotification(_ response: UNNotificationResponse) {
// Show reminder details
}
private func handleUpdateNotification(_ response: UNNotificationResponse) {
// Trigger app update check
}
private func handleGenericNotification(_ response: UNNotificationResponse) {
// Default handling
}
}