Problem with push notifications in production React Native

When I test the notification system in a test environment using expo go, everything works as it should. However, when I create an APK through expo and install it on the phone, everything works except when I click on the notification, there is a problem because it does not redirect to the finished screen.

Screen1.js:

const getStoredNotifications = async () => {
    const notifications = await AsyncStorage.getItem('notifications');
    return notifications ? JSON.parse(notifications) : [];
};

const storeNotification = async (notificationId) => {
    const notifications = await getStoredNotifications();
    const updatedNotifications = [...notifications, notificationId];
    await AsyncStorage.setItem('notifications', JSON.stringify(updatedNotifications));
};

const scheduleReminder = async (item, immediate = false) => {
    const notifications = await getStoredNotifications();
  
    if (!immediate && notifications.includes(item.id)) {
      console.log(`Reminder already scheduled for item: ${decode(item.name)}`);
      return;
    }

    const itemStartDateTime = new Date(`${item.date}T${item.startTime}`);
    const triggerDate = immediate ? new Date(Date.now() + 5 * 1000) : new Date(itemStartDateTime.getTime() - 60 * 60 * 1000);
  
    if (triggerDate > new Date()) {    
      const latitude = parseFloat(item.latitude);
      const longitude = parseFloat(item.longitude);
      
      console.log(`Parsed coordinates: Latitude = ${latitude}, Longitude = ${longitude}`);
      
      if (!isNaN(latitude) && !isNaN(longitude)) {
        const formattedItem = {
          id: item.id,
          name: item.name,
          description: item.description,
          date: item.date,
          time: `${item.startTime} - ${item.endTime}`,
          location: item.location,
          latitude: latitude,
          longitude: longitude,
          phone: item.phone,
          image: item.imagePath,
          price: item.ticketPrice,
        };

        console.log('Scheduling reminder with item:', JSON.stringify(formattedItem));
  
        const notificationId = await Notifications.scheduleNotificationAsync({
          content: {
            title: `Starting soon!`,
            body: `Your item ${formattedItem.name} starts ${immediate ? 'soon' : 'in 1 hour'}.`,
            data: { item: formattedItem, channelId: 'reminders' },
          },
          trigger: { date: triggerDate },
          channelId: 'reminders',
        });
  
        console.log(`Reminder scheduled for item: ${formattedItem.name} with ID: ${notificationId}`);
        if (!immediate) {
          await storeNotification(item.id);
        }
      } else {
        console.warn('Invalid coordinates received in reminder:', `Latitude: ${latitude}`, `Longitude: ${longitude}`);
      }
    } else {
      console.log(`Trigger date is in the past. No reminder scheduled for item: ${item.name}`);
    }
  };

const fetchStoredItems = useCallback(async () => {
    setLoading(true);
    try {
      const user = JSON.parse(await AsyncStorage.getItem('user'));
      if (!user) {
        Alert.alert("Not Logged In", "Please log in.");
        return;
      }
      const response = await fetch(`${config.BASE_URL}api/fetchStoredItems?userId=${user.id}`);
      const data = await response.json();
      if (data.success) {
        const validItems = data.items.filter(item => {
          const itemEndDate = moment(item.date).add(7, 'days');
          const isArchived = item.status === 'Archived';
          const isPastEndDate = moment().isAfter(itemEndDate);
  
          return !isArchived && !isPastEndDate;
        });
  
        setStoredItems(validItems);
  
        const notifications = await getStoredNotifications();
  
        const unprocessedItems = validItems.filter(item => !notifications.includes(item.id));
  
        for (const item of unprocessedItems) {
          await scheduleReminder(item);
        }
      } else {
        Alert.alert("Error", "Unable to retrieve stored items.");
      }
    } catch (error) {
      console.error('Failed to fetch stored items', error);
      Alert.alert("Error", "Network issue.");
    } finally {
      setLoading(false);
    }
  }, []);

const handleTestReminder = async () => {
    if (storedItems.length === 0) {
      Alert.alert("No Stored Items", "You currently have no stored items to test.");
      return;
    }
    const item = storedItems[0];
    await scheduleReminder(item, true);
};

<TouchableOpacity onPress={handleTestReminder} style={styles.testButton}>
          <Text style={styles.testButtonText}>Test Reminder</Text>
</TouchableOpacity>

App.js:

import React, { useEffect, useRef, useState } from 'react';
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';
import Navigation from './Components/StackNavigation';
import * as Notifications from 'expo-notifications';
import { navigationRef } from './Components/NavigationService';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { decode } from 'utf8';

SplashScreen.preventAutoHideAsync();

export default function App() {
  const [fontsLoaded, setFontsLoaded] = useState(false);
  const [isNavigating, setIsNavigating] = useState(false);
  const notificationListener = useRef(null);

  const checkNotificationPermissions = async () => {
    const { status } = await Notifications.getPermissionsAsync();
    let finalStatus = status;

    if (status !== 'granted') {
      const { status } = await Notifications.requestPermissionsAsync();
      finalStatus = status;
    }

    if (finalStatus !== 'granted') {
      Alert.alert('Permissions', 'Notification permission is not enabled.');
      return false;
    }

    return true;
  };

  const saveReminderData = async (item) => {
    try {
      const itemData = JSON.stringify(item);
      await AsyncStorage.setItem(`reminder_${item.id}`, itemData);
      console.log('Reminder data saved locally.');
    } catch (error) {
      console.error('Failed to save reminder data:', error);
    }
  };

  const removeReminderData = async (itemId) => {
    try {
      await AsyncStorage.removeItem(`reminder_${itemId}`);
      console.log('Reminder data removed.');
    } catch (error) {
      console.error('Failed to remove reminder data:', error);
    }
  };

  useEffect(() => {
    async function loadResourcesAndDataAsync() {
      try {
        const hasPermission = await checkNotificationPermissions();
        if (!hasPermission) {
          console.log('Notifications are not allowed');
        }

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

        const existingChannel = await Notifications.getNotificationChannelAsync('reminders');
        if (!existingChannel) {
          await Notifications.setNotificationChannelAsync('reminders', {
            name: 'Reminders',
            importance: Notifications.AndroidImportance.HIGH,
            vibrationPattern: [0, 250, 250, 250],
            lightColor: '#FF231F7C',
          });
          console.log('Notification channel created: reminders');
        } else {
          console.log('Notification channel already exists:', existingChannel);
        }

        await Font.loadAsync({
          // Fonts
        });

        const handleNotificationResponse = async (response) => {
          console.log('Notification response received:', response);
      
          const { identifier, content } = response.notification?.request || {};
          const channelId = content?.data?.channelId || 'reminders';
          console.log(`Notification ID: ${identifier}`);
          console.log(`Notification Channel: ${channelId}`);
      
          const item = content?.data?.item;

          console.log('Received item data:', item);

          if (item) {
            const formattedItem = {
              id: item.id,
              name: item.name,
              description: item.description || 'No description',
              date: item.date,
              time: item.time,
              location: item.location || 'N/A',
              latitude: parseFloat(item.latitude),
              longitude: parseFloat(item.longitude),
              phone: item.phone || 'No phone',
              image: item.image,
              price: item.price || 'N/A',
              openedFromNotification: true,
          };

              await removeReminderData(item.id);
      
              if (!isNaN(formattedItem.latitude) && !isNaN(formattedItem.longitude)) {
                  setIsNavigating(true);
                  navigationRef.current?.navigate('Item Details', { item: formattedItem });
                  setTimeout(() => setIsNavigating(false), 1000);
              } else {
                  console.warn('Invalid coordinates received in notification');
              }
          } else {
              console.error('Notification data is missing or invalid', item);
          }
      };

        const subscription = Notifications.addNotificationResponseReceivedListener(handleNotificationResponse);

        return () => {
          subscription.remove();
        };
      } catch (e) {
        console.warn(e);
      } finally {
        setFontsLoaded(true);
        SplashScreen.hideAsync();
      }
    }

    loadResourcesAndDataAsync();
  }, []);

  if (!fontsLoaded) {
    return null;
  }

  return <Navigation />;
}

When I run in production, and look at the logs, this shows me:

2024-08-11 23:31:35.473 19652-19690 EGL_emulation           com.example.MyApplication  D  app_time_stats: avg=34.05ms min=10.96ms max=140.43ms count=28
2024-08-11 23:31:37.991 19652-19690 EGL_emulation           com.example.MyApplication  D  app_time_stats: avg=176.36ms min=14.18ms max=2028.39ms count=14
2024-08-11 23:31:39.013 19652-19690 EGL_emulation           com.example.MyApplication  D  app_time_stats: avg=30.25ms min=2.52ms max=98.53ms count=30
2024-08-11 23:31:39.202 19652-19707 ReactNativeJS           com.example.MyApplication  I  Parsed coordinates: Latitude = 44.77935, Longitude = 20.439291
2024-08-11 23:31:39.523 19652-19707 ReactNativeJS           com.example.MyApplication  I  Notification scheduled for event: Children's Summer Games with ID: bd4741d9-416f-4e3b-87b6-7aac3fa6e574
2024-08-11 23:31:41.458 19652-19690 EGL_emulation           com.example.MyApplication  D  app_time_stats: avg=123.84ms min=2.02ms max=1899.74ms count=19
2024-08-11 23:31:42.483 19652-19690 EGL_emulation           com.example.MyApplication  D  app_time_stats: avg=20.06ms min=2.49ms max=33.73ms count=39
2024-08-11 23:31:44.474 19652-19807 expo-notifications      com.example.MyApplication  D  Notification request "bd4741d9-416f-4e3b-87b6-7aac3fa6e574" will not trigger in the future, removing.
2024-08-11 23:31:44.946 19652-19690 EGL_emulation           com.example.MyApplication  D  app_time_stats: avg=130.94ms min=2.37ms max=2005.04ms count=18
2024-08-11 23:31:45.967 19652-19690 EGL_emulation           com.example.MyApplication  D  app_time_stats: avg=26.19ms min=4.42ms max=58.36ms count=33
2024-08-11 23:31:48.521 19652-19652 ReactNativeJS           com.example.MyApplication  D  [native] ExpoNotificationLifecycleListener contains an unmarshaled notification response. Skipping.
2024-08-11 23:31:48.731 19652-19707 ReactNativeJS           com.example.MyApplication  I  'Notification response received:', { notification: 
                                                                                                       { request: 
                                                                                                          { trigger: 
                                                                                                             { channelId: null,
                                                                                                               value: 1723411904202,
                                                                                                               repeats: false,
                                                                                                               type: 'date' },
                                                                                                            content: 
                                                                                                             { autoDismiss: true,
                                                                                                               title: 'Starting Soon!',
                                                                                                               badge: null,
                                                                                                               sticky: false,
                                                                                                               sound: 'default',
                                                                                                               body: 'Your event Children's Summer Games is starting soon.',
                                                                                                               subtitle: null },
                                                                                                            identifier: 'bd4741d9-416f-4e3b-87b6-7aac3fa6e574' },
                                                                                                         date: 1723411904459 },
                                                                                                      actionIdentifier: 'expo.modules.notifications.actions.DEFAULT' }
2024-08-11 23:31:48.731 19652-19707 ReactNativeJS           com.example.MyApplication  I  Notification ID: bd4741d9-416f-4e3b-87b6-7aac3fa6e574
2024-08-11 23:31:48.731 19652-19707 ReactNativeJS           com.example.MyApplication  I  Notification Channel: reminders
2024-08-11 23:31:48.732 19652-19707 ReactNativeJS           com.example.MyApplication  E  'Notification data is missing or invalid', undefined
2024-08-11 23:31:48.883 19652-19708 unknown:ReactNative     com.example.MyApplication  E  console.error: Notification data is missing or invalid undefined, js engine: hermes, stack:
                                                                                                    _construct@1:141787
                                                                                                    Wrapper@1:141441
                                                                                                    _callSuper@1:139426
                                                                                                    SyntheticError@1:140918
                                                                                                    reactConsoleErrorHandler@1:140579
                                                                                                    ?anon_0_@1:733473
                                                                                                    asyncGeneratorStep@1:120466
                                                                                                    _next@1:120723
                                                                                                    anonymous@1:120675
                                                                                                    anonymous@1:120596
                                                                                                    handleNotificationResponse@1:733873
                                                                                                    anonymous@1:1623892
2024-08-11 23:31:49.140 19652-19690 EGL_emulation           com.example.MyApplication  D  app_time_stats: avg=206.85ms min=15.57ms max=2618.55ms count=15
2024-08-11 23:31:50.145 19652-19690 EGL_emulation           com.example.MyApplication  D  app_time_stats: avg=31.36ms min=2.32ms max=67.14ms count=25
2024-08-11 23:31:50.703 19652-19657 MyApplication           com.example.MyApplication  W  Cleared Reference was only reachable from finalizer (only reported once)
2024-08-11 23:31:50.747 19652-19657 MyApplication           com.example.MyApplication  I  Background concurrent mark compact GC freed 8462KB AllocSpace bytes, 22(1008KB) LOS objects, 49% free, 11MB/23MB, paused 1.575ms,2.825ms total 180.853ms
2024-08-11 23:31:52.680 19652-19690 EGL_emulation           com.example.MyApplication  D  app_time_stats: avg=137.83ms min=13.51ms max=2026.10ms count=18
2024-08-11 23:31:53.683 19652-19690 EGL_emulation           com.example.MyApplication  D  app_time_stats: avg=20.53ms min=2.39ms max=39.92ms count=37

New contributor

John is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật