Getting double notification popup for single event on ios when sending notification using FCM - react-native

Issue Description::
I am working on react native application and using react native firebase messaging service for push notification. I am having a problem on IOS platform. I am getting double notification popups for single event.
Steps I am following to generate the case::
After app installation if I am login and sending notification through FCM I just received a single popup. After this I logged out and login again, now this time I got double popups for single notification. In this case I am not clearing an app from background.
If after every logout I am clearing an app from background I just received a single popup for single event.
When I am logged out from the application, and forcefully sending notification from FCM, I am getting double popup on app initialization screen(login screen).
I am generating a new device token when user login and saving this token inside local storage, we are clearing local storage data on logout.
Code::
async mountDashboard() {
const enabled = await firebase.messaging().hasPermission();
if (enabled) {
const fcmToken = await firebase.messaging().getToken();
await AsyncStorage.setItem(LocalStorageKeys.DEVICE_TOKEN, fcmToken);
if (fcmToken) {
//--- here we are saving our token and sendind data to API
}
}
// in Foreground
this.notificationListener = firebase.notifications().onNotification((notification) => {
new RegisterLocalNotification(notification);
});
// App in Foreground and background
let notificationHandler = new NotificationsHandler();
this.notificationOpenedListener = firebase.notifications().onNotificationOpened((notificationOpen) => {
notificationHandler.handleNotification(notificationOpen.notification);
});
// app close notification handler
AppCloseNotificationHandler.getInstance().handleNotification();
}
componentDidMount() {
this.mountDashboard();
}
Environment::
Binaries:
Node: 10.15.0 - /usr/local/opt/node#10/bin/node
Yarn: 1.10.1 -/usr/local/bin/yarn
npm: 6.4.1 - /usr/local/opt/node#10/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
List item
npm Packages:
react: ^16.8.4 => 16.8.4
react-native: ^0.58.6 => 0.58.6
npm Global Packages:
react-native-cli: 2.0.1
react-native-firebase: 5.2.3

You have to unsubscribe from your listener when the component will unmount. If you don't do that you are subscribing two listeners.
componentWillUnmount() {
this.notificationListener(); // it's unsubscribing your listener
}

Make sure to send this payload via backend, I am using firebase admin SDK.
This will disable Notification By OS and trigger the local notification. I am using this package for local notification https://github.com/zo0r/react-native-push-notification
async function sendPushNotification(message) {
try {
await admin.messaging().sendToDevice(
['dCk27uEySymSdP_vA1C_QI:APA91bEZNyipZDjTLq0jrGnD0qcpKH2y3oTYg3GMgT0pSENNlJEiymOYXvxvnqTFtQaidDLt5KUgp4oDZsfLsQvfiVkL7m1bpMjekHsm-7x1ZDju4TYUMUZeUgYb0CyPwMhdr9RyzA1v'],
{
data: {
owner: 'a',
user: 'a',
},
},
{
// Required for background/quit data-only messages on iOS
contentAvailable: true,
// Required for background/quit data-only messages on Android
priority: 'high',
},
);
console.log('A');
}catch(e) {
console.log('Gagal mengirim pesan');
}
}
And this my code inside App.js that listen to the notification
RNFirebase.onMessage(RNFirebaseOnMessageHandler);
RNFirebase.setBackgroundMessageHandler(RNFirebaseOnMessageHandler);
And inside the handler, I use this
PushNotification.createChannel(
{
channelId: CHANNEL_ID,
channelName: CHANNEL_NAME,
channelDescription: CHANNEL_DESCRIPTION,
soundName: CHANNEL_SOUND,
importance: CHANNEL_IMPORTANCE,
vibrate: CHANNEL_VIBRATE,
},
(created) => console.log(`createChannel returned '${created}'`), // (optional) callback returns whether the channel was created, false means it already existed.
);
PushNotification.localNotification({
channelId: CHANNEL_ID,
// #todo get ticker value from data payload
ticker: 'CreateOrderLandingScreen',
showWhen: true,
autoCancel: true,
largeIcon: CHANNEL_LARGE_ICON,
smallIcon: CHANNEL_SMALL_ICON,
bigText: 'aaaaa',
subText: CHANNEL_SUB_TEXT,
color: Colors.RED,
vibrate: true,
vibration: 300,
priority: 'high',
visibility: 'private',
invokeApp: true,
alertAction: 'view',
id: 0,
title:'aa',
message: 'aaaa',
userInfo: {},
playSound: false,
soundName: 'default',
});

Related

React Native push local notification in background(killed)

I m coding an application who need push notification in background(app was killed).
its notification app for twitch.
I m store channels with redux persist(asyncstorage).
What should i do? do u have any documentation directly about that ?
You can use a push notification library like https://github.com/zo0r/react-native-push-notification and a background task library like https://github.com/transistorsoft/react-native-background-fetch. Then you can call your push notification(s) inside background tasks.
Keep in mind that the minimum interval between background tasks being run is 15 minutes. So the interval can only be 15 minutes or longer.
To make this answer more useful, here is a previous implementation I've used to handle push notifications in the background to give you an idea of how to go about it:
import PushNotification from 'react-native-push-notification';
export default class NotificationService {
constructor(onRegister, onNotification) {
this.configure(onRegister, onNotification);
this.lastId = 0;
}
// Handles a user push notification registration
configure(onRegister, onNotification, gcm = '') {
PushNotification.configure({
// (optional) Called when Token is generated (iOS and Android)
onRegister: onRegister,
// (required) Called when a remote or local notification is opened or received
onNotification: onNotification,
// ANDROID ONLY: GCM Sender ID (optional - not required for local notifications, but is need to receive remote push notifications)
senderID: gcm,
/**
* (optional) default: true
* - Specified if permissions (ios) and token (android and ios) will requested or not,
* - if not, you must call PushNotificationsHandler.requestPermissions() later
*/
requestPermissions: true,
});
}
// Send a direct push notification to the user
localNotif() {
this.lastId++;
PushNotification.localNotification({
/* Android Only Properties */
id: '' + this.lastId,
bigText: 'My big text that will be shown when notification is expanded',
subText: 'This is a subText',
/* iOS and Android properties */
title: 'Local Notification',
message: 'My Notification Message',
actions: '["Yes", "No"]', // (Android only) See the doc for notification actions to know more
});
}
// Schedules a push notification by a given javascript Date object
scheduleNotif(date, title, message) {
this.lastId++;
PushNotification.localNotificationSchedule({
date: date,
/* Android Only Properties */
id: '' + this.lastId,
bigText: '',
subText: '',
/* iOS and Android properties */
title: title,
message: message,
});
}
checkPermission(cbk) {
return PushNotification.checkPermissions(cbk);
}
cancelNotif() {
PushNotification.cancelLocalNotifications({ id: '' + this.lastId });
}
cancelAll() {
PushNotification.cancelAllLocalNotifications();
}
}
// ...
import BackgroundFetch from 'react-native-background-fetch';
const initBackGroundFetch = () => {
BackgroundFetch.configure(
{
minimumFetchInterval: 15, // <-- minutes (15 is minimum allowed)
// Android options
forceAlarmManager: false, // <-- Set true to bypass JobScheduler.
stopOnTerminate: false,
startOnBoot: true,
requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY, // Network connection needed
},
async taskId => {
// Do stuff with notifications, for example:
const notificationService = new NotificationService(
() => {//... what to do on register},
() => {//... what to do on notification }
)
const date = new Date(Date.now() + 60 * 1000) // adjust according to your use case
notificationService.scheduleNotif(date, "title", "message");
BackgroundFetch.finish(taskId);
},
error => {
console.log('[js] RNBackgroundFetch failed to start');
},
);
};
const App = () => {
useEffect(() => {
initBackGroundFetch();
}, []);
return (
// ...
);
};
The NotificationService is an adjusted version of an example the react native push notification library provides found here: https://github.com/zo0r/react-native-push-notification/blob/master/example/NotifService.js.

React native remote notification

I am trying to send a mobile push notification to my android device based on apex trigger, and I didn't see notification banner but in console I can see onNotification event fired and I can see my payload info.
I have followed In react native app react-native-push-notification library, all changes are done as per the document mention here https://www.npmjs.com/package/react-native-push-notification.
updated MobilePushServiceDevice whenever user open the with token and service type
On FCM created the project and follow this document https://firebase.google.com/docs/cloud-messaging/android/client
Salesforce create connected enable push notification added server key
here is react code
const notification = PushNotification.configure({
// (optional) Called when Token is generated (iOS and Android)
onRegister: function (token) {
//Send this token to salesforce
let createPayload = {
ServiceType: getSystemName() == 'Android' ? 'androidGcm' : 'apple',
ConnectionToken: token.token
};
// register device with salesforce
},
// (required) Called when a remote is received or opened, or local notification is opened
onNotification: function (notification) {
console.log("NOTIFICATION:", notification);
notification.finish(PushNotificationIOS.FetchResult.NoData);
},
// (optional) Called when Registered Action is pressed and invokeApp is false, if true onNotification will be called (Android)
onAction: function (notification) {
console.log("ACTION:", notification.action);
console.log("NOTIFICATION:", notification);
// process the action
},
// (optional) Called when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. (iOS)
onRegistrationError: function (err) {
console.error(err.message, err);
},
// IOS ONLY (optional): default: all - Permissions to register.
permissions: {
alert: true,
badge: true,
sound: true,
},
visibility: 'public',
// Should the initial notification be popped automatically
// default: true
popInitialNotification: true,
senderID: "1019984307481",
requestPermissions: true,
});
here is my apex code
`Messaging.PushNotification msg = new Messaging.PushNotification();
Map<String, Object> androidPayload = new Map<String, Object>();
Set<String> userSet = new Set<String>();
androidPayload.put('title', idVSnickname.get(cbuId)+' assigned you a task');
androidPayload.put('message', subject);
androidPayload.put('parentId', str[4].trim());
androidPayload.put('id', str[0].trim());
userSet.add(accIdVSuserId.get(accId));
msg.setPayload(androidPayload);
msg.send('Mimit', userSet);`

Send push notification using Expo

I am trying to send push notification using Expo, and I receive it. But there is no vibrate or sound and no pop up as well on my device. I am using Galaxy S9 with Android 9. I have not tried on Iphone yet.
Push notification is sent by nodejs and the user who installed the app will receive the push notification. User expo token is saved in firebase database. I succeed to save and fetch the token.
Below is from expo app
class App extends Component {
componentWillMount() {
firebase.initializeApp(config);
this.registerForPushNotificationsAsync();
}
async registerForPushNotificationsAsync(){
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
if (Platform.OS === 'android') {
Notifications.createChannelAndroidAsync('chat-messages', {
name: 'Chat messages',
sound: true,
priority: 'high', // was max
vibrate: [0, 250, 250, 250],
});
}
if (existingStatus !== 'granted') {
const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
finalStatus = status;
}
Below is from nodejs server-side
function sendMessage(to, title, body) {
const expo = new Expo();
let messages = [];
messages.push({
to, // Expo user token
body,
data: { withSome: 'data' },
ios: {
sound: true
},
android: {
"channelId": "chat-messages" //and this
}
})
let chunks = expo.chunkPushNotifications(messages);
let tickets = [];
(async () => {
for (let chunk of chunks) {
try {
let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
tickets.push(...ticketChunk);
} catch (error) {
console.error(error);
}
}
})();
}
Also could we redirect to web page when user click the push notification?
I see three problems on your backend code ( expo push notification docs for reference https://docs.expo.io/versions/latest/guides/push-notifications/):
According to the docs, there should be no ios or android properties on the request body;
sound should be either 'default' or null, instead of true;
You created the notification channel on the device, but when you send the notification, you forgot to tell which channel you are sending to.
All that said, your code that calls the expo push notifications api should look something like this:
messages.push({
to, // Expo user token
title: 'some title', //it is good practice to send title, and it will look better
body,
data: { withSome: 'data' },
priority: 'high', //to make sure notification is delivered as fast as possible. see documentation for more details
sound: true, //for iOS devices and android below 8.0
channelId: 'chat-messages', //for devices on android 8.0 and above
})
I hope this helps.

Correct setup with react-native-fcm

I feel like the docs at react-native-fcm are a bit of a mess and I am having a hard time figuring this out.
I currently have a production app and my android users are telling me they are not receiving notifications for events where they should be. So this is stressing me out a lot right now. On iOS everything seems fine.
In the react-native-fcm example app you can see the following:
//FCM.createNotificationChannel is mandatory for Android targeting >=8. Otherwise you won't see any notification
componentDidMount() {
FCM.createNotificationChannel({
id: 'default',
name: 'Default',
description: 'used for example',
priority: 'high'
})
}
Do I need to call FCM.createNotificationChannel()?? I mainly use remote notifications so is this relevant in any way?
Here is MY setup:
import FCM, {
FCMEvent,
NotificationType,
RemoteNotificationResult,
WillPresentNotificationResult,
} from 'react-native-fcm';
FCM.on(FCMEvent.Notification, async (notif) => {
// there are two parts of notif. notif.notification contains the notification payload, notif.data contains data payload
FCM.presentLocalNotification({
id: "new_message", // (optional for instant notification)
title: notif.fcm.title, // as FCM payload
body: notif.fcm.body, // as FCM payload (required)
sound: "default", // as FCM payload
priority: "high", // as FCM payload
click_action: "com.myapp.MyCategory", // as FCM payload - this is used as category identifier on iOS.
badge: 10, // as FCM payload IOS only, set 0 to clear badges
number: 10, // Android only
ticker: "My Notification Ticker", // Android only
auto_cancel: true, // Android only (default true)
large_icon: "ic_launcher", // Android only
icon: "ic_launcher", // as FCM payload, you can relace this with custom icon you put in mipmap
color: "blue", // Android only
vibrate: 300, // Android only default: 300, no vibration if you pass 0
wake_screen: true, // Android only, wake up screen when notification arrives
group: "group", // Android only
picture: "https://google.png", // Android only bigPicture style
ongoing: false, // Android only
my_custom_data:'my_custom_field_value', // extra data you want to throw
lights: true, // Android only, LED blinking (default false)
});
if(Platform.OS ==='ios'){
//optional
//iOS requires developers to call completionHandler to end notification process. If you do not call it your background remote notifications could be throttled, to read more about it see https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application.
//This library handles it for you automatically with default behavior (for remote notification, finish with NoData; for WillPresent, finish depend on "show_in_foreground"). However if you want to return different result, follow the following code to override
//notif._notificationType is available for iOS platfrom
switch(notif._notificationType){
case NotificationType.Remote:
notif.finish(RemoteNotificationResult.NewData) //other types available: RemoteNotificationResult.NewData, RemoteNotificationResult.ResultFailed
break;
case NotificationType.NotificationResponse:
notif.finish();
break;
case NotificationType.WillPresent:
notif.finish(WillPresentNotificationResult.All) //other types available: WillPresentNotificationResult.None
break;
}
}
});
FCM.on(FCMEvent.RefreshToken, (token) => {
try {
const { currentUser } = firebase.auth();
let updates = {};
updates[`/allUsers/serviceUsers/${currentUser.uid}/userInfo/fcmToken`] = token;
return firebase.database().ref().update(updates).catch(err => console.log('fcm refresh error', err))
} catch (e) {
console.log('couldnt update fcm refresh token', e)
}
});
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
class App extends Component {
componentWillMount() {
let config = {configgg}
!firebase.apps.length ? firebase.initializeApp(config) : firebase.app();
FCM.requestPermissions();
}
render() {
return (
<Provider store={store}>
<Router/>
</Provider>
);
}
}
export default App;
I mainly use remote notifications and it is critical for my app for it to work. Is there anything I am missing in my setup?
Any hint or suggestions will help me out a lot! Thanks!
EDIT:
Found this in adb logcat when receiving a notification (that did not show up)
NotificationService: No Channel found for pkg=com.lisdoworker, channelId=null, id=0, tag=GCM-Notification:9015992, opPkg=com.lisdoworker, callingUid=10487, userId=0, incomingUserId=0, notificationUid=10487, notification=Notification(channel=null pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x10 color=0x00000000 vis=PRIVATE)
Does this have to do with FCM.createNotificationChannel()??
Yeah, apparently createNotificationChannel was added in version 16 to support Android 8 and it is barely documented.

How to receive react native push notification in foreground and background

Im using
React Native Push Notification https://github.com/zo0r/react-native-push-notification
Currently, it will receive push notification after closing the app.
How can I pass my pushMessage(chat) into the notification?
How to make it receive push notification in foreground (when the app still open) and background (without opening the app) *currently it will receive the notification after closing the app
which part should I work on?
App.js
--------
PushNotification.configure({
// (required) Called when a remote or local notification is opened or received
onNotification: function(notification) {
console.log( 'NOTIFICATION:', notification );
// process the notification . * this will overwrite the My Notification 1 below
PushNotification.localNotification({
foreground: true,
title: "My Notification Title",
message: "My Notification 2", // (required)
});
// required on iOS only (see fetchCompletionHandler docs: https://facebook.github.io/react-native/docs/pushnotificationios.html)
notification.finish(PushNotificationIOS.FetchResult.NoData);
},
});
class App extends Component{
componentDidMount(){
const connection = signalr.hubConnection('https://api.abc.com');
const proxy = connection.createHubProxy('chatHub');
proxy.on('pushMessage', (chat) => {
PushNotification.localNotification({
foreground: true,
title: "My Notification Title",
message: "My Notification 1", // (required)
});
});
}
}