How do I send push notifications with expo and react native? - push-notification

I am writing a simple app, where I need to send push notifications. For instance, a user liked a post -> send a push notification; or a user commented under a post -> send a push notification; or a user sent you a message -> send a push notification.
I am using Notifications from expo-notifications. I have set up my Notifications.addNotificationReceivedListener and Notifications.addNotificationResponseReceivedListener. I tested them using Expo's push notification tool and it all works fine.
However, I am struggling to send notifications. As suggested per expo's docs they have a library for Node.js called expo-server-sdk-node which takes care of sending notifications. As per their doc:
The Expo push notification service accepts batches of notifications
so that you don't need to send 1000 requests to send 1000
notifications. We recommend you batch your notifications to reduce
the number of requests and to compress them (notifications with
similar content will get compressed).
And I agree with the above statement. Sending notifications on batch makes sense, however, I have a question regarding this:
How do I implement it? Do I keep counter on the user's notification, and lets say, the user has liked 10 posts -> then I send 10 notifications as a batch request? What if they liked 8 posts and then closed the app? Do I send the notifications on closing the app? It doesn't seem right to me. Also, if the user has sent a message, I believe I should straight away send the notification, rather than waiting for a batch request for the user to send 10 messages.
The implementation they offer on their docs is the following:
// Create the messages that you want to send to clients
let messages = [];
for (let pushToken of somePushTokens) {
// Each push token looks like ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]
// Check that all your push tokens appear to be valid Expo push tokens
if (!Expo.isExpoPushToken(pushToken)) {
console.error(`Push token ${pushToken} is not a valid Expo push token`);
continue;
}
// Construct a message (see https://docs.expo.io/push-notifications/sending-notifications/)
messages.push({
to: pushToken,
sound: 'default',
body: 'This is a test notification',
data: { withSome: 'data' },
})
}
Which is OK and understandable. But then I struggle with the next step:
let chunks = expo.chunkPushNotifications(messages);
let tickets = [];
(async () => {
for (let chunk of chunks) {
try {
let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
console.log(ticketChunk);
tickets.push(...ticketChunk);
} catch (error) {
console.error(error);
}
}
})();
That is, at which point do I call expo.chunkPushNotifications(messages) and then await expo.sendPushNotificationsAsync(chunk). Do I wait 10 similar notifications to be collected, do I wait some time, do I do it when the user closes the app, or something else?

Related

Show background notification using conditions - Flutter

I am developing an App where the user will receive a notification if any other user likes his picture.
I'm able to handle in-App notification and apply my conditions without any problem.
Unfortunately, I am not able to handle the background Notification.
This is what I did so far:
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
if (1 > 1) {
print('send it');
return true;
} else {
print('don\'t send it');
return false;
}
}
No matter what I do under _firebaseMessagingBackgroundHandler, notification will be shown anyways.
My handler is triggered and I can see the print in Console.
I believe there is another handle inside the FCM SDK that handles this notification, and the FirebaseMessaging.onBackgroundMessage is just an option.
Any thoughts? thank you!
The way you are trying to handle the background message is fine but there is more to know how the FCM really works.
First of all, Firebase Provide us two types of messaging
Notification Message
FCM automatically displays the message to end-user devices on behalf of the client app. Notification messages have a predefined set of user-
visible keys and an optional data payload of custom key-value pairs
Data Message
The client app is responsible for processing data messages. Data messages have
only custom key-value pairs with no reserved key names
Now for your scenario, you should send your message from Firebase as Data Message then you have control of it, otherwise, if you send the Notification Message from Firebase the notification will show automatically.
Here are example sample snippets of two different types of Firebase Message
Notification Message:
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"notification":{
"title":"Portugal vs. Denmark",
"body":"great match!"
}
}
}
Data Message
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"data":{
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
}
}
}
And finally, I recommend you take a look at the official documentation About FCM message.

Firebase Cloud Messaging Notification not received in web app

I am using FCM for notification. FCM gets triggered on creation of data from the Firebase database. I received first message. After that other consecutive messages is not received. I'm running this in a local environment. Is the problem due to the below message "Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions" or any other issue. Do I need to get into a billing plan for receiving messages. Working in test environment and that is the reason not moving to billing plan. If the issue is not related to billing plan can someone point any other problem with the code.
Firebase function log
6:22:52.133 PM
sendFollowerNotification
Function execution started
6:22:52.133 PM
sendFollowerNotification
Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions
6:22:52.143 PM
sendFollowerNotification
Function execution took 10 ms, finished with status: 'ok'
6:22:52.401 PM
sendFollowerNotification
1 messages were sent successfully
Node js code
exports.sendFollowerNotification = functions.database.ref('/notification/message/{gId}/{pId}')
.onCreate(async (change, context) => {
//console.log('Group id:', context.params.gId," Push ID:",context.params.pId, "Change",change);
const notificationData = change.val();
var topic = notificationData.topic;
var title = notificationData.title;
var body = notificationData.body;
var registrationTokens = notificationData.tokens;
const message = {
notification: {
title: title,
body: body
},
tokens: registrationTokens,
};
admin.messaging().sendMulticast(message)
.then((response) => {
// Response is a message ID string.
console.log(response.successCount + ' messages were sent successfully');
})
.catch((error) => {
console.log('Error sending message:', error);
});
});
That message does not indicate an error. It's just a warning letting you know that outbound networking does not work if your project is not on a payment plan. FCM messaging does not fall in this category - it should work.
The problem is that your code doesn't return a promise that resolves after all asynchronous work is complete. Right now, it returns nothing, and the function terminates immediately before the message is sent. Please read and understand the documentation about this.
Minimally, you will need to return the promise chain to let Cloud Functions know when the message is sent, and it's safe to terminate.
return admin.messaging().sendMulticast(message)
.then((response) => {
// Response is a message ID string.
console.log(response.successCount + ' messages were sent successfully');
})
.catch((error) => {
console.log('Error sending message:', error);
});
Note the return keyword above.
If the message still isn't being sent, then there is some other problem here that we can't see. You might not be handling your device tokens correctly.
I think this might answer your question: Why will I need a billing account to use Node.js 10 or later for Cloud Functions for Firebase?:
Because of updates to its underlying architecture planned for August 17, 2020, Cloud Functions for Firebase will rely on some additional paid Google services: Cloud Build, Container Registry, and Cloud Storage. These architecture updates will apply for functions deployed to the Node.js 10 runtime. Usage of these services will be billed in addition to existing pricing.
In the new architecture, Cloud Build supports the deployment of functions. You'll be billed only for the computing time required to build a function's runtime container.
On the other hand, the Service Firebase Clud Messaging itself is free:
Firebase Cloud Messaging (FCM) provides a reliable and battery-efficient connection between your server and devices that allows you to deliver and receive messages and notifications on iOS, Android, and the web at no cost.
Given that you are using Node in your CFs, the Platform requires to you a Billing Account.

Single Device Push Notifications (FCM Registration Token) not working

I'm using react-native-firebase with firebase-messaging to implement push notifications.
While I can send push notifications to everyone, I'm unable to send them to individuals. To my knowledge, all I need is the FCM Registration Token which I retrieve by following the documentation
firebase.messaging().getToken()
.then(fcmToken => {
if (fcmToken) {
console.log("fcm token", fcmToken);
} else {
console.log("fcm token", "null");
}
});
The token I receive is a plausible token, but when I copy that token from the logs into the firebase console and try to send it to a single device, nothing is received. (If I push to a user segment, it's received by all devices with the app installed).
As I can not test for iOS, this might or might not also be a problem on iOS.
In my build.gradle file, the firebase relevant libraries are included like so:
compile(project(':react-native-firebase')) {
transitive = false
}
implementation "com.google.android.gms:play-services-base:15.0.1"
implementation "com.google.firebase:firebase-core:16.0.1"
implementation 'com.google.firebase:firebase-auth:16.0.2'
implementation "com.google.firebase:firebase-messaging:17.1.0"
In the MainApplication.java file I add them to the list of ReactPackages.
new RNGoogleSigninPackage(),
new RNFirebasePackage(),
new RNFirebaseMessagingPackage(),
new RNFirebaseAuthPackage()
My AndroidManifest does not have any Firebase relevant changes as I discovered that push notifications were working even without them. (While trying to implement push notifications I noticed that all phones were receiving them except the one I was actively working on - so I rolled back the code.)
edit: I managed to receive one single device notification. Without any changes, it was suddenly working exactly once. Now it doesn't work anymore.

React native app crashes on receiving notification with error message "There is no completion handler with completionHandlerId:<Id>"

I am trying to integrate react-native-firebase to a simple App. I followed the steps mentioned here http://invertase.io/react-native-firebase/#/installation-ios?id=_11-initialisation. I configured everything and run the app, but when I get notification from fcm app crashes with above-mentioned error message and it happens in RNFirebaseMessaging.m line number 406,
the code looks like this
RCT_EXPORT_METHOD(finishRemoteNotification: (NSString *)completionHandlerId fetchResult:(UIBackgroundFetchResult)result) {
RCTRemoteNotificationCallback completionHandler = self.notificationCallbacks[completionHandlerId];
if (!completionHandler) {
RCTLogError(#"There is no completion handler with completionHandlerId: %#", completionHandlerId);
return;
}
completionHandler(result);
[self.notificationCallbacks removeObjectForKey:completionHandlerId];
}
I did print completionHandler dictionary before this method is called and it has data but when this method is called completionHandler is nil.
Has anyone come across the same issue?
I am using react native version 0.47.0 and react-native-firebase 2.0.5
PS: App crashes every time it receives remote notification.
As written here
The problem is that you are probably subscribing to 2 (or more) onMessage listeners.
When you call onMessage it will return unsubscribe method which you can call later if you need to and then subscribe again afterword.
Also, they are currently working on Messaging / FCM overhaul
// subscribe
const unsubscribe = firebase.messaging().onMessage((url) => {
// ...
});
// unsubscribe
unsubscribe();

Bluemix Push Notification with userID

I am using the latest Bluemix Push Notification service. I'm using the MFPPush API to register the device (Android).
Here is my code snippet:
var success = function(message)
{
console.log("Success: " + message);
alert("Reg Device: " + message);
};
var failure = function(message)
{
console.log("Error: " + message);
alert("Error: " + message);
};
MFPPush.registerDevice({}, success, failure);
var notification = function(notification)
{
// notification is a JSON object.
alert(notification.message);
};
MFPPush.registerNotificationsCallback(notification);
The success message contains the following information:
Token:APA91bFtkSr59Zxlr52HU****Uij
UserId: ""
DeviceId: g5c6d98f-0867-3fd1-a353-15bcdef675a2
When I send the notification, my device receives the message.
The Swagger REST API shows that I can arbitrarily give some token, userId and deviceId:
{
"deviceId": "TestDeviceId",
"platform": "G",
"token": "************",
"userId": "Joe"
}
How do I get the "TestDeviceId", and how do I get the "token"? I don't see any API to get that information.
Note: You should probably just use the Client SDK to register as it handles all of this in the background--automatically assigning each device a unique ID. You shouldn't explore this unless you know what you're doing. There isn't a really a reason for trying to manually set the deviceID.
When you register a device for the Push Notifications service, you set these values in the body of the POST request. On a successful call, it will return these values in the response. I'll do a demo of this later in the post.
You can also retrieve a list of the device registrations for the Push Notifications service.
You can use that deviceId to retrieved detailed information about the device, send a specific push notification to that device, subscribe it to a tag and send push notifications to those devices, etc.
Swagger Documentation is here.
Regarding those values, you can put whatever you want in them when you register. Typically, these values would be set automatically by the Bluemix Mobile Android/iOS Client SDK when you do the register call. However, you could do this manually using the REST client.
For example:
Here, I'm registering a device:
It registered successfully:
This is what I get if I ask the Push Notifications service for information about my registered devices (for the "deviceId": "arandomdeviceid"):
The Android BMS Core Client SDK sets this deviceId using a unique UUID from the device and hashing it with MD5.
You can look in here more more information about that.
#anamica (a) Allow userId parameter to be passed along with the MFPPush registration like MFPPush.register({"userId": "AUniqueUserId"}, success, failure) (b) Add an additional parameter to the target "userIds" (array).
This enhancement has been done, you can give a try by updating the latest SDK.

Resources