From dda1208570363cba97487b7af02cd195d87e9e12 Mon Sep 17 00:00:00 2001 From: vishwas kumar Date: Thu, 20 Aug 2026 14:47:03 +0530 Subject: [PATCH] fix(ios): stop hijacking UNUserNotificationCenter.delegate NotificationManager installed a NotificationDelegate wrapper on UNUserNotificationCenter.delegate to present the library notification while the host app is in the foreground. That property is weak and the wrapper was not retained anywhere, so Kotlin/Native collected it shortly after the first monitored request and the delegate silently became null. From that point the host app received no notification callbacks at all: push notifications stopped being presented in the foreground, and taps no longer reached userNotificationCenter(_:didReceive:withCompletionHandler:), which broke push deep links in a way that is very hard to trace back to a network debugging library. Retaining the wrapper fixes the null delegate but not the underlying problem: the delegate belongs to the host app, and taking it over means competing with whatever the app and its other SDKs install there, in an order the library cannot control. The library no longer touches the delegate, and NotificationDelegate is removed. The notification is still posted and still appears in Notification Center; it is simply no longer presented as a banner while the app is in the foreground, and tapping it no longer routes through the library. --- build.gradle.kts | 2 +- .../ui/notification/NotificationDelegate.kt | 40 ------------------- .../ui/notification/NotificationManager.kt | 9 +++-- 3 files changed, 7 insertions(+), 44 deletions(-) delete mode 100644 core/library/src/iosMain/kotlin/ro/cosminmihu/ktor/monitor/ui/notification/NotificationDelegate.kt diff --git a/build.gradle.kts b/build.gradle.kts index dca60bd2..05939b6c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ plugins { allprojects { group = "robustrade" - version = "1.14.0" + version = "1.14.2" } subprojects { diff --git a/core/library/src/iosMain/kotlin/ro/cosminmihu/ktor/monitor/ui/notification/NotificationDelegate.kt b/core/library/src/iosMain/kotlin/ro/cosminmihu/ktor/monitor/ui/notification/NotificationDelegate.kt deleted file mode 100644 index 9500799f..00000000 --- a/core/library/src/iosMain/kotlin/ro/cosminmihu/ktor/monitor/ui/notification/NotificationDelegate.kt +++ /dev/null @@ -1,40 +0,0 @@ -package ro.cosminmihu.ktor.monitor.ui.notification - -import platform.UserNotifications.UNNotification -import platform.UserNotifications.UNNotificationPresentationOptionAlert -import platform.UserNotifications.UNNotificationPresentationOptionBadge -import platform.UserNotifications.UNNotificationPresentationOptionSound -import platform.UserNotifications.UNNotificationPresentationOptions -import platform.UserNotifications.UNNotificationResponse -import platform.UserNotifications.UNUserNotificationCenter -import platform.UserNotifications.UNUserNotificationCenterDelegateProtocol -import platform.darwin.NSObject - -internal class NotificationDelegate( - private val appNotificationDelegate: UNUserNotificationCenterDelegateProtocol? = null -) : NSObject(), UNUserNotificationCenterDelegateProtocol { - - override fun userNotificationCenter( - center: UNUserNotificationCenter, - willPresentNotification: UNNotification, - withCompletionHandler: (UNNotificationPresentationOptions) -> Unit - ) { - val options = UNNotificationPresentationOptionAlert or - UNNotificationPresentationOptionSound or - UNNotificationPresentationOptionBadge - - appNotificationDelegate - ?.userNotificationCenter(center, willPresentNotification, withCompletionHandler) - ?: withCompletionHandler(options) - } - - override fun userNotificationCenter( - center: UNUserNotificationCenter, - didReceiveNotificationResponse: UNNotificationResponse, - withCompletionHandler: () -> Unit - ) { - appNotificationDelegate - ?.userNotificationCenter(center, didReceiveNotificationResponse, withCompletionHandler) - ?: withCompletionHandler() - } -} diff --git a/core/library/src/iosMain/kotlin/ro/cosminmihu/ktor/monitor/ui/notification/NotificationManager.kt b/core/library/src/iosMain/kotlin/ro/cosminmihu/ktor/monitor/ui/notification/NotificationManager.kt index d8b52359..ae6ad52b 100644 --- a/core/library/src/iosMain/kotlin/ro/cosminmihu/ktor/monitor/ui/notification/NotificationManager.kt +++ b/core/library/src/iosMain/kotlin/ro/cosminmihu/ktor/monitor/ui/notification/NotificationManager.kt @@ -13,9 +13,12 @@ import kotlin.coroutines.resume internal actual class NotificationManager { - private val notificationCenter = UNUserNotificationCenter.currentNotificationCenter().apply { - setDelegate(NotificationDelegate(appNotificationDelegate = delegate())) - } + // The library deliberately does not touch UNUserNotificationCenter.delegate. That property is + // the host app's, it is weak (so anything installed here has to be kept alive), and taking it + // over displaces whatever the app or its SDKs installed — which silently breaks push handling, + // deep links included. The notification below is still delivered to Notification Center; it is + // simply not presented as a banner while the app is in the foreground. + private val notificationCenter = UNUserNotificationCenter.currentNotificationCenter() actual suspend fun clear() { notificationCenter.removePendingNotificationRequestsWithIdentifiers(listOf(NOTIFICATION_ID.toString()))