React Native - Expo Notifications not received when app is closed

39 Views Asked by At

I'm using expo-notifications and Firebase Cloud Messaging (FCM) to handle push notifications in my React Native app. However, I'm encountering an issue where notifications only arrive when the app is in the foreground or background. They are not received when the app is completely closed.

Here's the relevant code:

App.tsx

import messaging from "@react-native-firebase/messaging";
import * as Notifications from "expo-notifications";
import { useEffect } from "react";
import { PermissionsAndroid, Platform } from "react-native";

Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: true,
  }),
});

export default function App() {
  const requestNotificationPermission = async () => {
    try {
      await messaging().requestPermission();
    } catch (error) {
      console.error("Error requesting notifications permission:", error);
    }
  };

  useEffect(() => {
    if (Platform.OS === "android") {
      PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
      );
    } else {
      requestNotificationPermission();
    }
  }, []);

  return (
    ...
    <BottomTabNavigator />
    ...
  )
}

BottomTabNavigator.tsx

import messaging from "@react-native-firebase/messaging";
import { NavigationProp, useNavigation } from "@react-navigation/native";
import * as Notifications from "expo-notifications";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import Toast from "react-native-toast-message";
import { useLoggedUser, ToastNotification, registerNotifications } from "@utils";

export function BottomTabNavigator() {
  const { t } = useTranslation();
  const { fetchUser, setDeviceId } = useLoggedUser();
  const navigation = useNavigation<NavigationProp<RootTabParamList>>();

  useEffect(() => {
    fetchUser();
    registerNotifications().then(
      (deviceId) => deviceId && setDeviceId(deviceId)
    );

    // CLICK HANDLER
    const handleNotificationClick = async (
      response: Notifications.NotificationResponse
    ) => {
      const screen = response?.notification?.request?.content?.data?.screen;
      if (screen !== null) {
        navigation.navigate(screen);
      }
    };

    // CLICK LISTENER
    const notificationClickSubscription =
      Notifications.addNotificationResponseReceivedListener(
        handleNotificationClick
      );

    // BACKGROUND HANDLER
    messaging().setBackgroundMessageHandler(async (remoteMessage) => {
      console.log("Message handled in the background!", remoteMessage);

      const notification = {
        title: remoteMessage.notification?.title,
        body: remoteMessage.notification?.body,
        data: remoteMessage.data, // optional data payload
      };

      // Schedule the notification with a null trigger to show immediately
      await Notifications.scheduleNotificationAsync({
        content: notification,
        trigger: null,
      });
    });

    // FOREGROUND HANDLER
    const handlePushNotification = async () => {
      ToastNotification({
        title: t("notifications.new"),
        type: "info",
        onPress: () => {
          navigation.navigate("Settings");
          Toast.hide();
        },
      });
    };

    // FOREGROUND LISTENER
    const unsubscribe = messaging().onMessage(handlePushNotification);

    // Clean up the event listeners
    return () => {
      unsubscribe();
      notificationClickSubscription.remove();
    };
  }, []);
}

I've noticed that if I populate the body of the push notification with notification object, I receive two notifications when the app is foreground or background: one from Expo and one from Firebase. To avoid this redundancy, I'm populating only the data object. However, this leads to notifications not being displayed when the app is closed (only the one from Firebase).
I need to use expo-notifications to handle the click on notification and group the messages.

I've tried two approaches:

  1. Populating both body and data objects:

    This resulted in receiving two notifications: one from Expo and one from Firebase. While the Firebase notification was still delivered even when the app was closed, it created redundancy in the foreground and background states.

  2. Populating only the body object with data:

    This avoided duplicate notifications, but notifications weren't displayed when the app was completely closed. This is the behavior I'm trying to overcome.

In both cases, I expected notifications to be delivered regardless of the app state (foreground, background, or closed).

Ideally, I'd like to achieve this functionality with data-only notifications, without creating duplicate notifications.

0

There are 0 best solutions below