From a5fa5f6ad9b8a13c288d1510d3a2f5c76f2921c9 Mon Sep 17 00:00:00 2001 From: sherwinski Date: Wed, 8 Jul 2026 13:43:26 -0700 Subject: [PATCH] fix: harden iOS click listener registration to match Android pattern --- .../Runtime/iOSNotificationsManager.cs | 50 ++++++++++++++++--- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/com.onesignal.unity.ios/Runtime/iOSNotificationsManager.cs b/com.onesignal.unity.ios/Runtime/iOSNotificationsManager.cs index a70a13606..265bcb638 100644 --- a/com.onesignal.unity.ios/Runtime/iOSNotificationsManager.cs +++ b/com.onesignal.unity.ios/Runtime/iOSNotificationsManager.cs @@ -91,23 +91,54 @@ string resultUrl public event EventHandler ForegroundWillDisplay; public event EventHandler PermissionChanged; - // Only set the native listner once + // Only set the native listener once private bool _clickNativeListenerSet; + private readonly object _clickRegistrationLock = new object(); + private EventHandler _clicked; + + /// + /// The native click listener is registered lazily on the first subscription. The native SDK + /// queues clicks that occur before a listener is added (e.g. a cold start from a notification + /// tap) and replays them when one is registered, so deferring registration until a C# handler + /// exists ensures those events are not dropped. The handler must be attached before the + /// native call, since the replay fires as soon as the listener registers. The flag is only + /// set after a successful native call so a transient failure can retry on the next + /// subscription. + /// public event EventHandler Clicked { add { - _clicked += value; - - if (!_clickNativeListenerSet) + lock (_clickRegistrationLock) { - _clickNativeListenerSet = true; - _oneSignalNotificationsSetClickCallback(_onClicked); + _clicked += value; + + if (!_clickNativeListenerSet) + { + try + { + _oneSignalNotificationsSetClickCallback(_onClicked); + } + catch + { + // Roll back so a failed subscription has no effect; retrying won't + // add the same handler twice. + _clicked -= value; + throw; + } + _clickNativeListenerSet = true; + } + } + } + remove + { + lock (_clickRegistrationLock) + { + _clicked -= value; } } - remove { _clicked -= value; } } private static iOSNotificationsManager _instance; @@ -211,7 +242,10 @@ private static void _onClicked(string notification, string resultActionId, strin NotificationClickEventArgs args = new NotificationClickEventArgs(notif, result); EventHandler handler = _instance._clicked; - UnityMainThreadDispatch.Post(state => handler(_instance, args)); + if (handler != null) + { + UnityMainThreadDispatch.Post(state => handler(_instance, args)); + } } private static void _fillNotifFromObj(ref iOSDisplayableNotification notif, object notifObj)