React-native-push-notification: Notification isn't shown in notification centre when the app is in background or closed on android

Created on 9 Jul 2020  路  17Comments  路  Source: zo0r/react-native-push-notification

I cannot tell for sure which version this started with. But apparently this started when Firebase became mandatory for receiving remote notifications.

Currently, we are using 3.5.2. This is happening on android devices with custom launchers that allow showing badge count on the app's icon.

So if we send such structure from our server:

{
    "notification": {
       "title": "Title of Your Notification",
       "body": "Body of Your Notification"
    },
    "data": {
        "badge": 35,
        "payload": ""
    }
}

we will see a notification in background or closed states, but the badge number will not be updated.

But if we send that structure from our server:

{
    "data": {
        "title": "Title of Your Notification",
        "message": "Body of Your Notification"
        "badge": 35,
        "payload": ""
    }
}

we will get badge number updated, but we will not see a notification. That worked before!

The problem here is when we send both data and notification fields overridden FirebaseMessagingService.onMessageReceived is not getting fired and we don't set badge count, but notification field is handled by the OS and we see a notification.
When just a data field is sent onMessageReceived is fired and we get updated badge count, but not visible notification in the notification centre.

You can find more information here - https://stackoverflow.com/a/56660363/8457882

How can it be solved? As I can understand there is no way if notification is provided. Are we able to add logic for showing notification in handleReceivedMessage method or any other place?

Most helpful comment

@webraptor yes, it works. @Dallas62 is right. I called .configure() inside a component. Also, I noticed that .localNotification() should be called outside a component.

All are working fine now except one thing. If the app was closed and I received a push notification, then tapped on it the app launched without having been called with this notification.

All 17 comments

Hi @timetarget
Sorry for the delay,
Data payload will not trigger a notification to the user, and both notification and data will not trigger onNotification.
What you can do is sending data only message, then trigger a local notification with information inside the data-payload.

onNotification: function (notification) {
    console.log("NOTIFICATION:", notification);

    PushNotification.localNotification(notification.data);
    notification.finish(PushNotificationIOS.FetchResult.NoData);
  },

Hi @timetarget
Sorry for the delay,
Data payload will not trigger a notification to the user, and both notification and data will not trigger onNotification.
What you can do is sending data only message, then trigger a local notification with information inside the data-payload.

onNotification: function (notification) {
    console.log("NOTIFICATION:", notification);

    PushNotification.localNotification(notification.data);
    notification.finish(PushNotificationIOS.FetchResult.NoData);
  },

@Dallas62 Yeah, right. It is going to work. But what about the closed state? How to notify a user?

Same issue for me ^^

Hi @timetarget
In closed state, this should work too.

@Dallas62 I tried. I'm not sure about Java side, but JS side is not working when an app is closed, I suppose. At least it is working in the background but isn't when the app is closed.

It's probably because you use .configure() inside a component.

Hi @timetarget
In closed state, this should work too.

It does work.

@Dallas62 I tried. I'm not sure about Java side, but JS side is not working when an app is closed, I suppose. At least it is working in the background but isn't when the app is closed.

Are you sure about that?

@webraptor yes, it works. @Dallas62 is right. I called .configure() inside a component. Also, I noticed that .localNotification() should be called outside a component.

All are working fine now except one thing. If the app was closed and I received a push notification, then tapped on it the app launched without having been called with this notification.

@webraptor yes, it works. @Dallas62 is right. I called .configure() inside a component. Also, I noticed that .localNotification() should be called outside a component.

All are working fine now except one thing. If the app was closed and I received a push notification, then tapped on it the app launched without having been called with this notification.

What did you get? Can you please share code.

I have run https://github.com/zo0r/react-native-push-notification/tree/master/example this example,

Schedule notification is working find in foreground or background state! but once I kill the app from task manager, it isn't firing the local schedule notification!

Hi @rishisoni07
The example project is working fine, can you share what you are doing ?

Components are not mounted in killed state, and might be mounted / loaded in background state. If you put logic inside components which aim to be run in background/killed state, notification or background task, there is many chances the logic will not run.

Example project use a method to attach an handler from a component, but it's working only in foreground/background (probably only in debug) and when the component is mounted.

All are working fine now except one thing. If the app was closed and I received a push notification, then tapped on it the app launched without having been called with this notification.

Hey @Dallas62. If you didn't get the issue I can rephrase. When you launch the app on tapping on a notification the app will not know that it was launched from that notification. So we cannot perform any actions/updatings based on data in the notification. It applies only for android and only for closed state.

Hi @timetarget,

Ok, I see. The problem comes from a change un 3.4 I guess.
Before, the data object was flatten and used to trigger notification based on data-only notifications.
Now, data are grouped into a data property.

The side effect of that, is what you describe. Also because local-notification in android doesn't have support of data for now. Another issue is the way notification number works with android is a bit different since they use a different paradigm (Android stack and sum number). The current shortcut badger doesn't support all launchers (too many manufacturers).

A workaround in your case is, might be:

  • Generate a unique notification ID.
  • Save data in async-storage using the notification ID as KEY.
  • Use that id on the local notification.
  • When the App start after pressing, use the async-storage to get the data (based on the notification ID.
  onNotification: async function (notification) {
    console.log("NOTIFICATION:", notification);

    if(!notification.userInteraction) {
       await AsyncStorage.setItem('notification-data-' + notification.data.id, JSON.stringify(notification.data));

       PushNotification.localNotification(notification.data);
    }
    else {
       const rawData = await AsyncStorage.getItem('notification-data-' + notification.id);
       const data = JSON.parse(rawData);

      // Do what you want
    }

    notification.finish(PushNotificationIOS.FetchResult.NoData);
  },

This is a short sample code, you might use popInitialNotification in some case:

PushNotification.popInitialNotification(async function(notification) {
     if(!notification.userInteraction) {
         return;
     }

     const rawData = await AsyncStorage.getItem('notification-data-' + notification.id);
     const data = JSON.parse(rawData);

     // Do what you want
});

This code assume that there is a unique ID sent from server-side, but can be handle locally.

@Dallas62 I don't understand the purpose of adding unique id on a server and storing notification in AsyncStorage. Why can't I just use .popInitialNotification() when the app's data is loaded like so:

PushNotification.popInitialNotification((notification) => {
     if(notification?.userInteraction) {
         // perform actions based on notification.data
     }
});

?

Do you see here any pitfalls?

Maybe I don't understand the issue. The code I provided is if you want to keep data after triggering a local notification (the ID can come from server or mobile application, this is not important).

If you want data and notification without using data-only notification, you can use the initial payload you send:

{
    "notification": {
       "title": "Title of Your Notification",
       "body": "Body of Your Notification"
    },
    "data": {
        "badge": 35,
        "payload": ""
    }
}

This will work with the issue you mentioned (badge not set)

Let me make a quick overview.

What functionality do I need?
1) Badges to be updated in 3 states (foreground, background, closed).
3) Notifications to be visible in 3 states.
2) Perform actions on tapping on a notification. A good example is routing. I tap on the notification and go to a particular page and load data from a server for a particular item. Information about page and item I take from notification payload.

What did I have before I opened the issue?
I send this payload:

{
    "notification": {},
    "data": {}
}

In the app:
_Foreground:_ I called .setApplicationIconBadgeNumber() and .localNotification() manually;
_Background:_ I saw a notification in Notification Centre but the app didn't know about notification, so I couldn't update badge count;
_Closed:_ The same as in Background.

What did you suggest?
Sent this payload:

{
    "data": {}
}

and update all I need manually. So in the app now:
_Foreground:_ I called .setApplicationIconBadgeNumber() and .localNotification() manually;
_Background:_ The same as in Foreground.
_Closed:_ Didn't work because I used .configure() inside a component.

I fixed that and faced the last issue. When I launched the app on tapping on a notification the app didn't know that it was launched from that notification. So I couldn't perform routing based on data in the notification.

From your suggestion, I extracted the main thing for my case - to use .popInitialNotification like so:

PushNotification.popInitialNotification((notification) => {
     if(notification?.userInteraction) {
         // perform routing based on notification.data
     }
});

when the app launches (I cannot use it next to configure() method because when I get a notification in the closed state it will be called. A part of the app will be launched). I did shallow testing and it worked fine. The question here - do you see any pitfalls in this solution?

If this solution is working for your case, you can use it without hesitation.
I'm not sure if you can pass data to local-notification, I will check that (scheduled it's not working).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ahmadallo1 picture ahmadallo1  路  3Comments

cidevant picture cidevant  路  3Comments

nidzovito picture nidzovito  路  3Comments

Kiran0791 picture Kiran0791  路  3Comments

cookienawer picture cookienawer  路  3Comments