Correct setup with react-native-fcm - react-native

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.

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.

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

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',
});

IOS Expo Push Notifications when app is in foreground

reading from the expo docs:
For iOS, you would be wise to handle push notifications that are
received while the app is foregrounded, because otherwise the user
will never see them. Notifications that arrive while the app are
foregrounded on iOS do not show up in the system notification list. A
common solution is to just show the notification manually. For
example, if you get a message on Messenger for iOS, have the app
foregrounded, but do not have that conversation open, you will see the
notification slide down from the top of the screen with a custom
notification UI.
What I don't understand is what is the best approach for that? is there an Expo API for showing such messages? or should I create an alert component of my own? It is not really clear from the docs.
Thanks.
This answer is outdated as of February 20, 2020. Please see https://stackoverflow.com/a/60344280/2441420 for how to show iOS Notification when your application is in the Foreground
There isn't an Expo API for showing those messages. You can use any 'toast' library of your choosing and display the notification message, but that should be all your code.
For example, this is how we are doing right now:
export default class HomeScreen extends React.Component {
componentDidMount() {
this.notificationSubscription = Notifications.addListener(
(notification) => this.handlePushNotification(notification),
);
}
handlePushNotification(notification) {
const { navigation } = this.props;
PushNotificationsService.handleNotification(notification, navigation);
}
(...)
import Toast from 'react-native-root-toast';
export default class PushNotificationsService {
static handleNotification(notification, navigation) {
if (notification.data.screen && notification.origin === 'selected') {
navigation.navigate(notification.data.screen);
}
Toast.show(notification.data.message);
}
}
Toast libraries include:
react-native-root-toast
react-native-easy-toast
react-native-simple-toast
Now you can just add that in one of your app entry point. The shouldShowAlert is what you want here
import * as Notifications from 'expo-notifications';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
App.json :
{
"expo": {
"notification": {
"iosDisplayInForeground": true
}
}
DEMO
I'm not sure exactly when this was added to Expo, but as of Expo version 36 is easily doable.
To show Expo Push Notifications on iOS when your app is in the foreground, please do the following:
import { Vibration } from "react-native";
import { Notifications } from "expo";
import * as Permissions from "expo-permissions";
import Constants from "expo-constants";
registerForPushNotificationsAsync = async () => {
if (Constants.isDevice) {
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Permissions.askAsync(
Permissions.NOTIFICATIONS
);
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
let token = await Notifications.getExpoPushTokenAsync();
console.log("Go to https://expo.io/notifications and copy the token below to easily send yourself a notification.");
console.warn("Notifications on iOS (and I believe Android) ONLY WORK ON A PHYSICAL DEVICE, not a simulator or emulator!!!")
console.log(token);
this.setState({ expoPushToken: token });
} else {
alert("Must use physical device for Push Notifications");
}
};
componentDidMount() {
this.registerForPushNotificationsAsync();
this._notificationSubscription = Notifications.addListener(
this._handleNotification
);
}
_handleNotification = async notification => {
if (notification.remote) {
Vibration.vibrate();
const notificationId = Notifications.presentLocalNotificationAsync({
title: "Follow #technoplato",
body: "To learn yourself goodly (also follow PewDiePie)",
ios: { _displayInForeground: true } // <-- HERE'S WHERE THE MAGIC HAPPENS
});
}
};
Quick and Easy Sanity Check
1) Go here: https://expo.io/notifications
2) Copy the token that is output to the terminal when your application is run.
3) Open your application on iOS.
4) Send a notification to yourself from https://expo.io/notifications and observe that it shows up even when your app is foregrounded.
Notes
Notifications WILL NOT BE RECEIVED ON AN IOS SIMULATOR
Expo makes Notifications ridiculously easy. I honestly can't believe it.
No idea why displayInForeground is false by default and not more prominent in the documentation. I'll submit a PR for it if I can.
Code originally found at this Snack: https://snack.expo.io/#documentation/pushnotifications?platform=ios
LocalNotification.ios._displayInForeground found here: https://docs.expo.io/versions/v36.0.0/sdk/notifications/#localnotification