Open nikitasigal opened 3 weeks ago
Haven't been able to test on iOS 18 yet, I don't have Xcode 16. I tested on iOS 17.6, and I only received one message. I'll circle back to this once I have Xcode 16.
Hello! Yes, I experienced the same running on iOS 18. I've tried some workarounds like this one:
//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
// HACK for iOS 18: We got duplicated push. One with an uppercase identifier and the other lowercase.
// So we report only one push.
NSString *versionString = [[UIDevice currentDevice] systemVersion];
if ([versionString floatValue] < 18.0 || ![self.latestPushId isEqualToString:notification.request.identifier]) {
// Still call the JS onNotification handler so it can display the new message right away
NSDictionary *userInfo = notification.request.content.userInfo;
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo
fetchCompletionHandler:^void (UIBackgroundFetchResult result){}];
}
[self setLatestPushId:notification.request.identifier];
completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}
Basically it saves the latest received identifier of the push notification and skip it if it is the same, but I've received some other non-ui pushes in-between that changed the latest identifier, like:
UI Push - Identifier: A Non-UI Push - Identifier: B (Duplicated) UI Push: Identifier A
So the push with 'B' identifier will the logic above to fail.
Another workaround, would be to have in memory the list of all the push identifiers and check if they were previously reported. But this is not a good idea because it can be a huge list in memory.
But this is more a patch than fixing the real bug.
Any other suggestions would be welcome!
Ah! This is happening when receiving the push notification while the app is in foreground.
Btw, looks like to be an issue in iOS 18 Beta investigated by Apple: https://developer.apple.com/forums/thread/762412?answerId=800720022#800720022
And here is a possible patch/workaround: https://developer.apple.com/forums/thread/762126?answerId=800296022#800296022
So here is the updated code to filter out pushes that we don't use to show in the UI and to save the latest id to filter duplicated pushes:
//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
// HACK for iOS 18: We got duplicated push. One with an uppercase identifier and the other lowercase.
// So we report only one push.
// https://github.com/invertase/react-native-firebase/issues/7979
bool shouldDisplayPush = notification.request.content.title.length > 0 && notification.request.content.body.length > 0 && [notification.request.content.badge intValue] > 0 && notification.request.content.sound != nil;
NSString *versionString = [[UIDevice currentDevice] systemVersion];
if ([versionString floatValue] < 18.0 || ![self.latestPushId isEqualToString:notification.request.identifier]) {
// Still call the JS onNotification handler so it can display the new message right away
NSDictionary *userInfo = notification.request.content.userInfo;
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo
fetchCompletionHandler:^void (UIBackgroundFetchResult result){}];
}
// Save only if we should display it
if (shouldDisplayPush) {
[self setLatestPushId:notification.request.identifier];
}
completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}
Issue
On devices and simulators running iOS 18, the
onMessage()
callback gets called two or more times. The issue does not appear on Android or on iOS devices running 17.5 or below.I've verified, that the
onMessage()
call itself is only done once, so there should not be multiple listeners active. The remoteMessage payload isnotification+data
.Devices tested, where the bug is present:
Devices tested, where the bug is not present:
Project Files
Javascript
Click To Expand
#### `package.json`: ```json "dependencies": { "@gorhom/bottom-sheet": "^4.5.1", "@hmscore/react-native-hms-push": "^6.12.0-301", "@react-native-clipboard/clipboard": "^1.14.1", "@react-native-community/netinfo": "^11.3.2", "@react-native-firebase/app": "^20.4.0", "@react-native-firebase/messaging": "^20.4.0", "@react-navigation/bottom-tabs": "^6.5.9", "@react-navigation/native": "^6.1.8", "@react-navigation/native-stack": "^6.10.0", "@sentry/react-native": "^5.25.0", "@shopify/flash-list": "^1.6.1", "@types/semver": "^7.5.8", "axios": "^1.5.1", "mobx": "^6.10.2", "mobx-persist-store": "^1.1.3", "mobx-react-lite": "^4.0.5", "moment": "^2.29.4", "patch-package": "^8.0.0", "qs": "^6.12.2", "react": "18.2.0", "react-native": "0.73.8", "react-native-bootsplash": "^5.5.3", "react-native-calendars": "^1.1306.0", "react-native-code-push": "^8.3.1", "react-native-config": "^1.5.1", "react-native-device-info": "^10.11.0", "react-native-gesture-handler": "^2.17.0", "react-native-linear-gradient": "^2.8.3", "react-native-mask-input": "^1.2.3", "react-native-mmkv": "^2.10.2", "react-native-modal": "^13.0.1", "react-native-permissions": "^4.1.5", "react-native-reanimated": "^3.6.0", "react-native-safe-area-context": "^4.4.1", "react-native-screens": "^3.25.0", "react-native-svg": "^13.14.0", "react-native-svg-transformer": "^1.1.0", "react-native-tab-view": "^3.5.2", "react-native-toast-notifications": "^3.4.0", "semver": "^7.6.3" }, ``` #### `firebase.json` for react-native-firebase v6: ```json # N/A ```
iOS
Click To Expand
#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like: ```ruby def node_require(script) # Resolve script with node to allow for hoisting require Pod::Executable.execute_command('node', ['-p', "require.resolve( '#{script}', {paths: [process.argv[1]]}, )", __dir__]).strip end node_require('react-native/scripts/react_native_pods.rb') node_require('react-native-permissions/scripts/setup.rb') platform :ios, min_ios_version_supported prepare_react_native_project! setup_permissions([ 'Notifications', ]) # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded # # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` # ```js # module.exports = { # dependencies: { # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), # ``` # flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled flipper_config = FlipperConfiguration.disabled use_frameworks! :linkage => :static $RNFirebaseAsStaticFramework = true target 'iteco_employee' do config = use_native_modules! use_react_native!( :path => config[:reactNativePath], # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and # you should disable the next line. :flipper_configuration => flipper_config, # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/.." ) target 'iteco_employeeTests' do inherit! :complete # Pods for testing end post_install do |installer| # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( installer, config[:reactNativePath], :mac_catalyst_enabled => false ) end end ``` #### `AppDelegate.m`: ```objc #import "AppDelegate.h" #import "RNBootSplash.h" #import
#import
#import
#import
#import
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleName = @"iteco_employee";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = [RNFBMessagingModule addCustomPropsToUserProps:nil withLaunchOptions:launchOptions];
[FIRApp configure];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [CodePush bundleURL];
#endif
}
- (NSURL *)getBundleURLf
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [CodePush bundleURL];
#endif
}
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initProps:(NSDictionary *)initProps {
UIView *rootView = [super createRootViewWithBridge:bridge moduleName:moduleName initProps:initProps];
[RNBootSplash initWithStoryboard:@"BootSplash" rootView:rootView]; // ⬅️ initialize the splash screen
return rootView;
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
@end
```
Android
Click To Expand
#### Have you converted to AndroidX? - [ ] my application is an AndroidX application? - [ ] I am using `android/gradle.settings` `jetifier=true` for Android compatibility? - [ ] I am using the NPM package `jetifier` for react-native compatibility? #### `android/build.gradle`: ```groovy // N/A ``` #### `android/app/build.gradle`: ```groovy // N/A ``` #### `android/settings.gradle`: ```groovy // N/A ``` #### `MainApplication.java`: ```java // N/A ``` #### `AndroidManifest.xml`: ```xml ```
Environment
Click To Expand
**`react-native info` output:** ``` System: OS: macOS 15.0 CPU: (10) arm64 Apple M2 Pro Memory: 88.00 MB / 16.00 GB Shell: version: "5.9" path: /bin/zsh Binaries: Node: version: 22.5.1 path: /opt/homebrew/bin/node Yarn: Not Found npm: version: 10.8.2 path: /opt/homebrew/bin/npm Watchman: version: 2024.07.15.00 path: /opt/homebrew/bin/watchman Managers: CocoaPods: version: 1.15.2 path: /opt/homebrew/bin/pod SDKs: iOS SDK: Platforms: - DriverKit 24.1 - iOS 18.1 - macOS 15.1 - tvOS 18.0 - visionOS 2.0 - watchOS 11.0 Android SDK: API Levels: - "31" - "33" - "34" Build Tools: - 30.0.3 - 31.0.0 - 33.0.0 - 33.0.1 - 34.0.0 - 35.0.0 System Images: - android-34 | Google APIs ARM 64 v8a Android NDK: Not Found IDEs: Android Studio: 2024.1 AI-241.18034.62.2411.12071903 Xcode: version: 16.1/16B5001e path: /usr/bin/xcodebuild Languages: Java: version: 17.0.12 path: /usr/bin/javac Ruby: version: 3.3.4 path: /opt/homebrew/opt/ruby/bin/ruby npmPackages: "@react-native-community/cli": Not Found react: installed: 18.2.0 wanted: 18.2.0 react-native: installed: 0.73.8 wanted: 0.73.8 react-native-macos: Not Found npmGlobalPackages: "*react-native*": Not Found Android: hermesEnabled: true newArchEnabled: false iOS: hermesEnabled: true newArchEnabled: false ``` - **Platform that you're experiencing the issue on**: - [x] iOS - [ ] Android - [ ] **iOS** but have not tested behavior on Android - [ ] **Android** but have not tested behavior on iOS - [ ] Both - **`react-native-firebase` version you're using that has this issue:** - `20.4.0` - **`Firebase` module(s) you're using that has the issue:** - `messaging` - **Are you using `TypeScript`?** - `Y`, `5.0.4`
React Native Firebase
andInvertase
on Twitter for updates on the library.