From 2a2bcc3fb23c4297eca6229383e9c8adcbeb342c Mon Sep 17 00:00:00 2001 From: Erisu Date: Wed, 13 Nov 2024 17:34:21 +0900 Subject: [PATCH] fix(ios)!: duplicate notification presentation on iOS 18.0 --- src/ios/PushPlugin.m | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/ios/PushPlugin.m b/src/ios/PushPlugin.m index 2d6917cad..991d37baf 100644 --- a/src/ios/PushPlugin.m +++ b/src/ios/PushPlugin.m @@ -35,6 +35,7 @@ @interface PushPlugin () @property (nonatomic, strong) NSDictionary *launchNotification; @property (nonatomic, strong) NSDictionary *notificationMessage; @property (nonatomic, strong) NSMutableDictionary *handlerObj; +@property (nonatomic, strong) UNNotification *previousNotification; @property (nonatomic, assign) BOOL isInline; @property (nonatomic, assign) BOOL clearBadge; @@ -346,6 +347,28 @@ - (void)willPresentNotification:(NSNotification *)notification { void (^completionHandler)(UNNotificationPresentationOptions) = notification.userInfo[@"completionHandler"]; + if (@available(iOS 18.0, *)) { + if (!@available(iOS 18.1, *)) { + // Note: In iOS 18.0, there is a known issue where "willPresentNotification" is triggered twice for a single payload. + // The "willPresentNotification" method is normally triggered when a notification is received while the app is in the + // foreground. Due to this bug, the notification payload is delivered twice, causing the front-end to process the + // notification event twice as well. This behavior is unintended, so this block of code checks if the payload is a + // duplicate by comparing the payload content and the timestamp of when it was received. + NSLog(@"[PushPlugin] Checking for duplicate notification presentation."); + if ([self isDuplicateNotification:originalNotification]) { + NSLog(@"[PushPlugin] Duplicate notification detected; processing will be skipped."); + if (completionHandler) { + completionHandler(UNNotificationPresentationOptionNone); + } + // Cleanup to remove previous notification to remove leaks + self.previousNotification = nil; + return; + } + // If it was not duplicate, we will store it to check for the potential second notification + self.previousNotification = originalNotification; + } + } + self.notificationMessage = modifiedUserInfo; self.isInline = YES; [self notificationReceived]; @@ -726,7 +749,26 @@ - (void)checkUserHasRemoteNotificationsEnabledWithCompletionHandler:(nonnull voi }]; } +- (BOOL)isDuplicateNotification:(UNNotification *)notification { + BOOL isDuplicate = NO; + if (self.previousNotification) { + // Extract relevant data from the current notification + NSDate *currentNotificationDate = notification.date; + NSDictionary *currentPayload = notification.request.content.userInfo; + // Extract relevant data from the previous notification + NSDate *previousNotificationDate = self.previousNotification.date; + NSDictionary *previousPayload = self.previousNotification.request.content.userInfo; + // Compare the date timestamp + BOOL isSameDate = [currentNotificationDate isEqualToDate:previousNotificationDate]; + // Compare the payload content + BOOL isSamePayload = [currentPayload isEqualToDictionary:previousPayload]; + isDuplicate = isSameDate && isSamePayload; + } + return isDuplicate; +} + - (void)dealloc { + self.previousNotification = nil; self.launchNotification = nil; self.coldstart = nil; }