I've created an app to test push notifications in a flutter app.
I am able to send a notification from the firebase messaging console and I can receive it as well in the foreground and background.
Once I've done this, I moved to the next step which is to send it automatically using the firebase cloud messaging service and I've used javascript and I've deployed function and it gets executed without any problem.
But the problem is that I can't receive a notification like this:-
but when I open my app since I've configured the firebase messaging inside initState(); I can see the notification and data get printed as well but I can't receive it like the photo above.
What should I do?
try to have a background handler or a top-level method as firebase messaging plugin's read me file says.
About my javascript index.js file:
It sends a notification for all the tokens in the pushTokens collection when a new document added to the posts collection and it does this but the problem is that I've mentioned above.
Index.js:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var notificationMessageData;
exports.fcmTester = functions.firestore.document('posts/{postID}').onCreate((snapshot, context) => {
const notificationMessageData = snapshot.data();
return admin.firestore().collection('pushTokens').get()
.then(snapshot => {
var tokens = [];
if (snapshot.empty) {
console.log('No Devices');
throw new Error('No Devices');
} else {
for (var token of snapshot.docs) {
tokens.push(token.data().tokenID);
}
var payload = {
"notification": {
"title": "from" + notificationMessageData.writer,
"body": "from" + notificationMessageData.name,
"sound": "default"
},
"data": {
"sendername": notificationMessageData.writer,
"message": notificationMessageData.name
}
}
return admin.messaging().sendToDevice(tokens, payload)
}
})
.catch((err) => {
console.log(err);
return null;
})
});
Put your app in background and try sending notification again bcoz flutter firebase messaging plug-in will not create notification if you are using the app while the notification has been sent although you can see its data in but to generate notification you have to do it manually if user is currently using the app.
On the other cases plug-in will create notification like if app is terminated or its in background.
Related
Is it possible to send FCM notifications through Firebase Cloud Functions, when a Firestore data field changes, but for a website, not an app. There is lots of guidance out there for Android and iOS but nothing for simply web apps, outside of sending notifications from the Firebase Console).
I've been trying to find out how to trigger a notification from Cloud Functions but can't find anything useful.
As an example, my database has the following structure:
Collection: users
Documents: documents named using userID
Data Fields: Fields 1 through 5. Field 5 stores the FCM Token. Field 1 stores their status (online, offline, offline pending messages).
I would like to ensure that when Data Field 1 changes (to 'offline pending messages), that the relevant user gets notified (based on the Doc ID).
Edit: adding code below for reference
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/users/{doc}/{Hears}')
.onUpdate(async (change, context) => {
const db = admin.firestore();
db.collection('users').doc(context.params.userId) // get userId
.get()
.then(doc => {
//this is intended to get the FCM token stored in the user's document
const fcmToken = doc.data().usrTkn;
// Notification details
const payload = {
notification: {
title: 'You have a new message.',
body: 'Open your app'
}
};
})
//This should send a notification to the user's device when web app is not in focus.
//FCM is set up in service worker
const response = await admin.messaging().sendToDevice(fcmToken, payload);
console.log(response);
});
Sending messages to a web app is no different from sending it to a native mobile app, so the sending part of guidance you've found is equally applicable. The Firebase documentation even contains an example of sending notifications on a Realtime Database trigger, and doing the same for Firestore would not be much different.
If you're having a specific problem sending messages, I recommend showing what you tried, and what isn't working about it.
Update: your code doesn't work (no matter what sort of device you send the notification to), because you're not handling the asynchronous nature of get() in your code.
The simplest way to fix that is to use await there too, just like you do when calling sendToDevice. So:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/users/{doc}/{Hears}')
.onUpdate(async (change, context) => {
const db = admin.firestore();
const doc = await db.collection('users').doc(context.params.userId).get();
const fcmToken = doc.data().usrTkn;
const payload = {
notification: {
title: 'You have a new message.',
body: 'Open your app'
}
};
const response = await admin.messaging().sendToDevice(fcmToken, payload);
console.log(response);
})
I highly recommend spending some time on learning about asynchronous calls, closures, async/await, and how to debug something like this by adding logging.
I am trying to send a push notification every time a child is created with no success.
I am creating a child with 2 token names with a question mark between them and trying to send to those tokens the notification.
to get the tokens from the phones I am using
new FirebaseMessaging().getToken() .
here is the firebase functions code
`
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Cloud Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
exports.onNewMessage = functions.database.
ref('/messages/{pushId}')
.onCreate((snapShot,context)=>{
var str = snapShot.key();
var res = str.split("?");
// Notification details.
const payload = {
notification: {
title: 'title!',
body: `body!`,
click_action: 'FLUTTER_NOTIFICATION_CLICK'
}
};
// Send notifications to all tokens.
admin.messaging().sendToDevice(res[0], payload);
admin.messaging().sendToDevice(res[1], payload);
});` .
This may have many if-thens, but I will describe here the most common sources of errors
1) Did not grant permissions for notifications for iOS/Android platform. For Android it is fine, and relatively easy to receive notifications, but for iOS you need Developer account to do that (on December 2019 it was 99$ per year)
2) I would recommend using topic subscription instead of tokenization (i.e. .getToken()) as it removes burden of following every single sent message manually
For example:
final fbmsg = FirebaseMessaging();
fbmsg.requestNotificationPermissions();
fbmsg.configure(onMessage: (msg) {
print(msg);
return;
}, onLaunch: (msg) {
print(msg);
return;
}, onResume: (msg) {
print(msg);
return;
});
fbmsg.subscribeToTopic('chats');
You can configure onLaunch, onResume, and onMessage behaviors on your own demand
For (1) and (2), a great place to start is following documentation of firebase_messaging library
3) I am not sure about this, but I think a better way to use index.js file could be using the snapshot that you receive (or at least try console.log() of whatever you get to check validity). But if it works for you, just ignore this step :) Below I attach the code from my app with working notifications
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.myFunction = functions.firestore
.document('chats/{message}')
.onCreate((snapshot, context) => {
return admin.messaging().sendToTopic('chats', {
notification: {
title: snapshot.data().username,
body: snapshot.data().text,
clickAction: 'FLUTTER_NOTIFICATION_CLICK'
},
});
});
4) I had hard time with establishing this Firebase Functions feature, also check installation steps for them as well
5) Check how you are trying to send the notification, first try to simulate it from the console, make sure that receiving part works, and then try to create an automated one
Hope it helped!
I have implemented firebase cloud messaging for my react-native app and now I am able to send and receive Notifications.
But now I want to get the Notifications Data like messages, right after clicking on it.
Why I need this ?
Because I have A simple chat app, and suppose I have three rooms, room1, room2, room3.
Now my App is closed and I receive Notification from room1, then I click on that, At this time I expected it open my app and navigate to the room1 chatbox, and the other rooms notifications too.
Any help?
note: I am using react-native-firebase v6
Cloud Messaging is only used to send messages from a server on the phone.
Before, on firebase 5, we had a package called "notifications" which allowed us to manage the interception of data when you clicked on it.
Since Firebase 6, this package doesn't exist anymore (well, in a way it will become paying and this service is called Notifee but it is still in test).
You have to use external packages such as react-native-push-notifications which allows you to intercept push notifications data.
async componentDidMount() {
this.createNotificationListeners();
}
async createNotificationListeners() {
this.notificationListener = firebase.notifications().onNotification((notification) => {
console.log(':::::::::::::::::::::::::::: APPLICATION OPEN MODE :::::::::::::::::::::::::::');
console.log(notification, 'APPLICATION OPEN');
// Manage Notifiacation
// firebase.notifications().removeDeliveredNotification(notification._notificationId);
});
const channel = new firebase.notifications.Android.Channel('fcm_FirebaseNotifiction_default_channel', 'JobApp', firebase.notifications.Android.Importance.High)
.setDescription('DEMO NOTIFICATION DESCRIPTION');
firebase.notifications().android.createChannel(channel);
this.notificationOpenedListener = firebase.notifications().onNotificationOpened((notificationOpen) => {
console.log(':::::::::::::::::::::::::::: APPLICATION WORKING IN BACKGROUND MODE :::::::::::::::::::::::::::');
console.log(notificationOpen.notification.data);
const { notificationType } = notificationOpen.notification.data;
console.log(notificationType)
firebase.notifications().removeDeliveredNotification(notificationOpen.notification._notificationId);
});
const notificationOpen = await firebase.notifications().getInitialNotification();
if (notificationOpen) {
console.log(':::::::::::::::::::::::::::: APPLICATION CLOSED :::::::::::::::::::::::::::');
console.log(notificationOpen);
}
this.messageListener = firebase.messaging().onMessage((message) => {
console.log(JSON.stringify(message));
});
}
I'm using multiple databases in a Firebase project. Cloud functions for the main (default) database work great, however, I cannot make them work for a secondary database. For example I want to make a read request on a node with admin privileges:
//this works
admin.database().ref(nodePath).once('value')...
This works in the main database, however, if I want to execute the command on another database, it doesn't work:
//this doesn't work
admin.database(secondaryDatabaseUrl).ref(nodePath).once('value')...
Although the functions are deployed, I get an error on the console when trying to execute the cloud function.
Here's the code for the cloud function with an https trigger:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const secureCompare = require('secure-compare');
exports.testFunction= functions.https.onRequest((req, res) => {
const key = req.query.key;
// Exit if the keys don't match
if (!secureCompare(key, functions.config().cron.key)) {
console.error('keys do not match');
res.status(403).send('error1');
return;
}
//test read request
//the line below crashes the function
return admin.database('https://secondary_db_url.firebaseio.com').ref(`/testNode`).once('value').then(dataSnapshot=> {
console.log('value', dataSnapshot.val());
return;
}).catch(er => {
console.error('error', er);
res.status(403).send('error2');
});
});
Below is the error log in the Firebase console:
TypeError: ns.ensureApp(...).database is not a function
at FirebaseNamespace.fn (/user_code/node_modules/firebase-admin/lib/firebase-namespace.js:251:42)
at exports.testFunction.functions.https.onRequest (/user_code/index.js:16:16)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/providers/https.js:26:41)
at /var/tmp/worker/worker.js:671:7
at /var/tmp/worker/worker.js:655:9
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickDomainCallback (internal/process/next_tick.js:128:9)
If I don't specify the secondary database URL, the function will make the read request on my main database which works great:
//this works
return admin.database().ref(`/testNode`).once('value').then(dataSnapshot=> {
...
I'm using the latest SDK versions: "firebase-admin": "^5.5.1" and "firebase-functions": "^0.7.3"
So, how do I get an instance of a secondary database in cloud functions using admin privileges?
Here's how to access database by URL using Admin SDK:
let app = admin.app();
let ref = app.database('https://secondary_db_url.firebaseio.com').ref();
Here's an example from Admin SDK integration tests: https://github.com/firebase/firebase-admin-node/blob/master/test/integration/database.js#L52
With cloud functions > 1.1 now, here is the documentation link that saved my life on this issue.
https://firebase.google.com/docs/database/usage/sharding#connect_your_app_to_multiple_database_instances
So, it looks like this at the top of my my cloud function index.js :
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const dev = admin.initializeApp({
databaseURL: "https://appdev.firebaseio.com"
}, 'dev');
const v2 = admin.initializeApp({
databaseURL: "https://appv2.firebaseio.com"
}, 'v2');
and then, in my clond functions functions code I can do :
//will change stuff on default database
admin.database().ref().child(`stuff/${stuffId}`).set(myStuff)
//will change stuff on my dev database
admin.database(dev).ref().child(`stuff/${stuffId}`).set(myStuff)
//will change stuff on my v2 database
admin.database(v2).ref().child(`stuff/${stuffId}`).set(myStuff)
So it looks like you are trying to access multiple databases using the javascript web client API. Passing the URL of the database to the API like this doesn't work with the Admin SDK:
admin.database('https://secondary_db_url.firebaseio.com').ref(`/testNode`)
Instead, you have to initialize a second app, give it a name, and pass that app around to the Admin SDK APIs. Here's a complete sample that writes the same data to two different database instances in the same project:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
const otherConfig = Object.assign({}, functions.config().firebase)
otherConfig.databaseURL = 'https://your-other-db.firebaseio.com/'
const otherApp = admin.initializeApp(otherConfig, 'otherAppName')
exports.foo = functions.https.onRequest((req, res) => {
const data = { foo: 'bar' }
const p1 = admin.database().ref('data').set(data)
const p2 = admin.database(otherApp).ref('data').set(data)
Promise.all([p1, p2]).then(() => {
res.send("OK")
})
.catch(error => {
res.status(500).send(error)
})
})
Updating this while on Firebase Functions v3.14.0. None of this answers worked for me so I implemented this solution
instance Registers a function that triggers on events from a specific Firebase Realtime Database instance
functions.database.instance('my-app-db-2').ref('/foo/bar')
Use the name of the database instance and it works, no need for the url. functions.database.ref used without instance watches the default instance for events.
So if both the answers doesn't work.
What happened with me is both the method worked without any error but second instance of database was not getting updated.
I updated npm and firebase CLI it worked.
Also #Dough Stevenson you Passing the URL of the database to the API like this **does** work with the Admin SDK
And this is a good blog from Firebase about the same
Firebase Blog : Easier scaling with multi-database support!
I'm using Cloud Functions for Firebase to send notifications to the user. I'm able to get the notification but the problem is that everyone is getting the notification, I'm trying to send the notification to a particular user. I'm saving user's device id in Firebase's database and then send that particular person the notification. Here is my code:
To save user's data, which is actually working fine:
DatabaseReference root = FirebaseDatabase.getInstance().getReference();
DatabaseReference groupsRef = root.child("users").child(Settings.Secure
.getString(ctx.getContentResolver(), Settings.Secure.ANDROID_ID));
groupsRef.child("isLogin").setValue(2);
In first activity subscribing to the topic:
FirebaseMessaging.getInstance().subscribeToTopic("android");
And finally javascript code(something I know very little about):
var functions = require('firebase-functions');
var admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/users')
.onWrite(event => {
var eventSnapshot = event.data;
var str = "This is notification"
console.log(str);
var topic = "android";
var payload = {
data: {
isLogin: eventSnapshot.child("975af90b767584c5").child("isLogin").val()
}
};
return admin.messaging().sendToTopic(topic, payload)
.then(function (response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
Here instead of "975af90b767584c5" which is hardcoded right now, I want to send device id, which I don't know how to do in javascript. Or if there is any other way.
Any help is appreciated, thanks.
First in app, get User FCM token by
String Token = FirebaseInstanceId.getInstance().getToken();
Now, send this token to your server and save it with other user details in Database.
Then when you want to send Notification to specific use, fetch that user's FCM token from Database via user id or something else.If you want to send notification to multiple user then fetch multiple user FCM token from database and put it in arrayList.
Now for main part, call fcm endpoint with your KEY, notification content
and most important: token or token array.
In your case, do not use sendToTopic, use send to: Token/Array
You can google for java script syntax, but this is main logic.For more info:
https://firebase.google.com/docs/cloud-messaging/admin/send-messages