Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

On iOS onMessage event is not firing in Foreground #7772

Open
2 of 10 tasks
tiborazo opened this issue May 1, 2024 · 10 comments
Open
2 of 10 tasks

On iOS onMessage event is not firing in Foreground #7772

tiborazo opened this issue May 1, 2024 · 10 comments
Labels
Help: Needs Triage Issue needs additional investigation/triaging. Impact: Bug New bug report

Comments

@tiborazo
Copy link

tiborazo commented May 1, 2024

Issue

Hi All,

I am trying to fix this for months without success. If anyone can help, it would be much appreciated.

Trying to change the push notifications package used by an old app to the latest React Native and Firebase messaging. I have set up a new app from scratch to isolate the issue - all new identifiers, bundle ids, certificates, etc. The only things installed in the new project are yarn add @react-native-firebase/app and @react-native-firebase/messaging

On Android everything works. On iOS background and killed state messages are also working, but onMessage event is never fired when in Foreground 😢

I followed all the latest guides on https://reactnative.dev/docs/environment-setup , https://rnfirebase.io/ , https://rnfirebase.io/messaging/usage and https://rnfirebase.io/messaging/usage/ios-setup.

I am sending the notifications with AWS SNS, but it is the same when sending from the Firebase Console too.

Relevant part of the App.tsx:

function App(): React.JSX.Element {

  useEffect(() => {
    (async () => await requestUserPermission() )();
    const unsubscribe = messaging().onMessage(async remoteMessage => {
      console.log('A new FCM message arrived!', JSON.stringify(remoteMessage));
      Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
    });

    return unsubscribe;
  }, []);

In Podfile the following lines have been added:

  config = use_native_modules!

  use_frameworks! :linkage => :static

Nothing has been changed in index.js


Project Files

Javascript

Click To Expand

package.json:

{
  "name": "TiborTestingPushNotifications",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "lint": "eslint .",
    "start": "react-native start",
    "test": "jest"
  },
  "dependencies": {
    "@react-native-firebase/app": "^19.2.2",
    "@react-native-firebase/messaging": "^19.2.2",
    "react": "18.2.0",
    "react-native": "0.73.7"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@babel/preset-env": "^7.20.0",
    "@babel/runtime": "^7.20.0",
    "@react-native/babel-preset": "0.73.21",
    "@react-native/eslint-config": "0.73.2",
    "@react-native/metro-config": "0.73.5",
    "@react-native/typescript-config": "0.73.1",
    "@types/react": "^18.2.6",
    "@types/react-test-renderer": "^18.0.0",
    "babel-jest": "^29.6.3",
    "eslint": "^8.19.0",
    "jest": "^29.6.3",
    "prettier": "2.8.8",
    "react-test-renderer": "18.2.0",
    "typescript": "5.0.4"
  },
  "engines": {
    "node": ">=18"
  }
}

firebase.json for react-native-firebase v6:

# N/A

App.tsx:

import React, { useEffect } from 'react';
import type { PropsWithChildren } from 'react';
import {
  Alert,
  SafeAreaView,
  ScrollView,
  StatusBar,
  StyleSheet,
  Text,
  useColorScheme,
  View,
} from 'react-native';

import {
  Colors,
  DebugInstructions,
  Header,
  LearnMoreLinks,
  ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
import messaging from '@react-native-firebase/messaging';

async function requestUserPermission() {
  const authStatus = await messaging().requestPermission();
  const enabled =
    authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
    authStatus === messaging.AuthorizationStatus.PROVISIONAL;

  if (enabled) {
    const token = await messaging().getAPNSToken();
    console.log('TOKEN:', token);
  }
}

type SectionProps = PropsWithChildren<{
  title: string;
}>;

function Section({ children, title }: SectionProps): React.JSX.Element {
  const isDarkMode = useColorScheme() === 'dark';
  return (
    <View style={styles.sectionContainer}>
      <Text
        style={[
          styles.sectionTitle,
          {
            color: isDarkMode ? Colors.white : Colors.black,
          },
        ]}>
        {title}
      </Text>
      <Text
        style={[
          styles.sectionDescription,
          {
            color: isDarkMode ? Colors.light : Colors.dark,
          },
        ]}>
        {children}
      </Text>
    </View>
  );
}

function App(): React.JSX.Element {

  useEffect(() => {
    (async () => await requestUserPermission())();
    const unsubscribe = messaging().onMessage(async remoteMessage => {
      console.log('A new FCM message arrived!', JSON.stringify(remoteMessage));
      Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
    });

    return unsubscribe;
  }, []);

  const isDarkMode = useColorScheme() === 'dark';

  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
  };

  return (
    <SafeAreaView style={backgroundStyle}>
      <StatusBar
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
        backgroundColor={backgroundStyle.backgroundColor}
      />
      <ScrollView
        contentInsetAdjustmentBehavior="automatic"
        style={backgroundStyle}>
        <Header />
        <View
          style={{
            backgroundColor: isDarkMode ? Colors.black : Colors.white,
          }}>
          <Section title="Step One">
            Edit <Text style={styles.highlight}>App.tsx</Text> to change this
            screen and then come back to see your edits???
          </Section>
          <Section title="See Your Changes">
            <ReloadInstructions />
          </Section>
          <Section title="Debug">
            <DebugInstructions />
          </Section>
          <Section title="Learn More">
            Read the docs to discover what to do next:
          </Section>
          <LearnMoreLinks />
        </View>
      </ScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
  },
  sectionDescription: {
    marginTop: 8,
    fontSize: 18,
    fontWeight: '400',
  },
  highlight: {
    fontWeight: '700',
  },
});

export default App;

iOS

Click To Expand

ios/Podfile:

  • I'm not using Pods
  • I'm using Pods and my Podfile looks like:
# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
  'require.resolve(
    "react-native/scripts/react_native_pods.rb",
    {paths: [process.argv[1]]},
  )', __dir__]).strip

platform :ios, min_ios_version_supported
prepare_react_native_project!

linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
  Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
  use_frameworks! :linkage => linkage.to_sym
end

target 'TiborTestingPushNotifications' do
  config = use_native_modules!

  use_frameworks! :linkage => :static
  
  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 'TiborTestingPushNotificationsTests' 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.mm:

#import "AppDelegate.h"
#import <Firebase.h>
#import <React/RCTBundleURLProvider.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [FIRApp configure];
  self.moduleName = @"TiborTestingPushNotifications";
  // 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 = @{};

  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
  return [self getBundleURL];
}

- (NSURL *)getBundleURL
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

@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:

// N/A

android/app/build.gradle:

// N/A

android/settings.gradle:

// N/A

MainApplication.java:

// N/A

AndroidManifest.xml:

<!-- N/A -->


Environment

Click To Expand

react-native info output:

info Fetching system and libraries information...
(node:44054) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
System:
  OS: macOS 14.4.1
  CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
  Memory: 57.55 MB / 16.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 21.2.0
    path: /usr/local/bin/node
  Yarn:
    version: 1.22.17
    path: /usr/local/bin/yarn
  npm:
    version: 10.2.3
    path: /usr/local/bin/npm
  Watchman:
    version: 2024.04.15.00
    path: /usr/local/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /Users/azoitunesconnect/.rbenv/shims/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.4
      - iOS 17.4
      - macOS 14.4
      - tvOS 17.4
      - visionOS 1.1
      - watchOS 10.4
  Android SDK: Not Found
IDEs:
  Android Studio: 2023.2 AI-232.10227.8.2321.11479570
  Xcode:
    version: 15.3/15E204a
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.10
    path: /usr/bin/javac
  Ruby:
    version: 3.2.2
    path: /Users/azoitunesconnect/.rbenv/shims/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.73.7
    wanted: 0.73.7
  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:
    • 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:
    • e.g. 19.2.2
  • Firebase module(s) you're using that has the issue:
    • e.g. Messaging
  • Are you using TypeScript?
    • Y & 5.0.4


@tiborazo tiborazo added Help: Needs Triage Issue needs additional investigation/triaging. Impact: Bug New bug report labels May 1, 2024
@Shervanator
Copy link

Shervanator commented May 1, 2024

Seems like the same issue as: #7770

I am also noticing this... onMessage is not firing very often (was working reliably in the past), I am occasionally getting it to fire on iOS, but can't identify anything in particular thats making it work?

I did upgrade to react-native-firebase v19.2.2 yesterday (was previously on v16) but rolling back to the old version doesn't seem to help...

I wonder if this is something to do with Firebase/Apple? 🤔

(btw everything is working fine on Android)

@Shervanator
Copy link

Okay I have noticed one slightly strange thing...

  1. Install fresh version of my app
  2. Register for provisional push notifications
  3. Get FCM token and send a message via Firebase messaging UI => I get nothing when the app is open
  4. Force close App
  5. Send a message via Firebase messaging UI => I see the provisional message
  6. Tap the message => Opens the app
  7. Send a message via Firebase messaging UI => onMessage fires successfully

So it seems like after clicking the firebase message once will result in onMessage starting to fire correct again on iOS? 🤔

@tiborazo
Copy link
Author

tiborazo commented May 1, 2024

Hi @Shervanator ,

Thank you for taking the time to reply, much appreciated.

It seems similar to the issue you quoted #7770 but still slightly different in this case, because

  • background messages are working
  • the push notifications are not data only.

It is interesting what you described above with what is happening after Force close and sending again. Unfortunately, that is not working for me. I briefly tried that with both of our apps and didn't seem to work. The only difference in what I did is me sending from AWS SNS - later I will try using the Firebase console too.

I was playing around with setting "messaging_ios_auto_register_for_remote_messages": false and registering manually in the code with await messaging().registerDeviceForRemoteMessages(); but that didn't make any difference.

I will let you know if there is any progress.

Thank you,
Tibor

@Shervanator
Copy link

Okay I've narrowed my problem down, not sure if its similar to what you are seeing:

  1. Fresh install app
  2. Register for provisional push notifications
  3. Send message containing data to app
  4. onMessage does not fire
  5. Force close app
  6. Send message containing data to app
  7. onMessage now does fire

So maybe I'm running into a different issue, but that is my reproduction steps...

@mikehardy
Copy link
Collaborator

Very interesting - that's good testing...

  • would be interesting to know the exact FCM contents you can post the FCM REST API (with a different device token, of course) to trigger this behavior so anyone trying things can know with certainty they are reproducing exactly the same
  • it appears something about the app device API usage around notification registration or token creation or something similar has an issue. Maybe a race condition maybe something else? It looks like there is a missing step 1a of "open app first time", and 5b "open app again" since we are talking about foreground onMessage receipt yes?
  • something that is happening either in step 2 permission post / accept and 5b "open app again" (app initializes...) needs to also happen either in 1a "open app first time" or after 2 (permission post / accept) it seems?

@Shervanator
Copy link

Thanks for the reply @mikehardy!

  1. The payload I'm currently sending for this test is via the firebase messaging console UI, its a simple message with a title, body, and a data payload. I realise this isn't really data-only, but at least on Android (and I believe on iOS in the past) the app would still have onMessage called so it could handle the push notification without showing the alert in the notification centre. Tomorrow I can try sending a payload via the FCM server side api and send through the exact payload...
  2. Yep this is in regards to foreground onMessage
  3. I'll have a little peak into this to see if I find any more interesting info...

@tiborazo
Copy link
Author

tiborazo commented May 8, 2024

Thank you @Shervanator & @mikehardy for taking the time to check, it is really appreciated.

In my case the onMessage is working correctly when sending the notification from the Firebase Console.

Unfortunately, switching to sending through Firebase is not an option. Our backend have AWS SNS built in and no intention to re-structure it.

Is there a way through APNS payload to force the onMessage to react? This is what we are sending:

{
	"aps": {
		"alert": {
			"title": "Tibor testing",
			"body": "Test001"
		}
	},
	"url": "https://www.azonetwork.com"
}

I tried to add "sound": "default" and "badge":1 - still the same.

Any help would be appreciated.

@mikehardy
Copy link
Collaborator

Unsure but I think you may need the high priority keys? A differential comparison of what your system sends vs what works when you send via the FCM REST API (so you have complete control of the JSON, critical here during testing) is how I'd go about it

@tiborazo
Copy link
Author

Thank you so much guys, it is now working 🎉

The onMessage function worked correctly when notifications were sent from the Firebase console, but our backend uses AWS SNS, and switching to using Firebase REST API would be too complicated.

Investigating further, I found useful discussions in old GitHub issues (e.g. #3479 and #5908) addressing problems with the gcm.message_id key.

Rather than altering the code in RNFBMessaging+UNUserNotificationCenter.m by removing the if (notification.request.content.userInfo[@"gcm.message_id"]) condition (which risks being overwritten during updates), I opted to add a dummy gcm.message_id to the payload. This solution proved to be effective.

{
  "aps": {
    "alert": {
      "title": "Sample Title",
      "body": "Sample Message"
    }
  },
  "url": "https://example.com",
  "gcm.message_id": "123"
}

I wish it was mentioned in the documentation, it would save tons of time - maybe this summary will help others going forward.

@ReactNativeDev2296
Copy link

Hello,
You must add the following code to the appropriate iOS files if the foreground isn't functioning in iOS.
AppDelegate.m
//Called when a notification is delivered to a foreground app. -(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { if (@available(iOS 14.0, *)) { completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionBanner | UNNotificationPresentationOptionList | UNNotificationPresentationOptionBadge); } else { completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge); } }

index.js
const unsubscribeNotification = messaging().onMessage(async (remoteMessage) => { if (Platform.OS === "ios") { PushNotificationIOS.addNotificationRequest({ id: remoteMessage?.messageId + "", title: remoteMessage?.data?.message || "", body: remoteMessage?.notification?.title || "", userInfo: remoteMessage?.data, isCritical: true, isSilent: false, sound: 'default' }); } else { this.displayNotification(remoteMessage); } });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Help: Needs Triage Issue needs additional investigation/triaging. Impact: Bug New bug report
Projects
None yet
Development

No branches or pull requests

4 participants