Reputation: 11753
I am working with an iOS app which calls an Firebase cloud function, to store FCM tokens, for later use when sending notifications. The problem is that it does not work.
I am using a Cloud Firestore database.
When the function is called, here is what I want to happen. The function checks the parameters against a database. If the data in the provided parameter is already found in the DB then nothing should happen. If it is not found, then it should be added to the DB.
My cloud function code is below. I would be glad if someone could help me find the precise issue.
exports.addNewElement = functions.https.onCall((data, context) => {
console.log('--addNewElement-- has been called with:\n' + data.fcmToken + '\n!!');
var collectionRef = db.collection("THE_COLLECTION");
// Create a query against the collection.
var query = collectionRef.where("fcmToken", "==", data.fcmToken);
query.get().then(function(doc) {
if (doc.exists) { // We should do nothing.
console.log("Document data:", doc.data());
} else { // We should add the new element.
// doc.data() will be undefined in this case
console.log("No such document!");
collectionRef.add({"fcmToken": fcmToken})
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
});
I could imagine other directions to handle FCM tokens. Is there any recommend way to use as best practice?
Upvotes: 4
Views: 5231
Reputation: 555
I take a bit of a different approach. I have an ionic app and after the app has registered with FCM (we have FCM token), I add the token to a 'devices' collection straight from the app. This way, a user can log in to more then one device and we will have the token for each device enabling us to send the message to each device. If you want to send a message to a user, you query the devices collection for that uid to get all the tokens for that user
Saving the token:
private saveTokenToFirestore(person: Person, token) {
if (!token) return;
const devicesRef = this.afs.collection('devices')
const docData = {
token,
userId: person.id,
}
return devicesRef.doc(token).set(docData)
}
where person.id is the firebase uid.
I then use firebase functions to monitor some nodes to figure out when to send FCM messages.
e.g. we have teams with persons as members and they can chat to each other. When a person sends a message to a team, each team member (except the sender himself) needs to get a notification.
Sending a notification to all members except the sender himself:
exports.chatMessageOnCreateSendFcm = functions.firestore
.document('chatGroups/{teamId}/messages/{messageId}')
.onCreate(async (snap, context) => {
const data = snap.data();
const teamId = context.params.teamId;
const name = data.pName;
const message = data.msg;
const userId = data.pId;
// Notification content
const payload = {
notification: {
title: name,
body: message,
}
}
const db = admin.firestore();
// get the team (chatGroup)
const teamRef = db.collection('teams').doc(teamId);
const teamSnapshot = await teamRef.get();
const team = teamSnapshot.data();
const devicesRef = db.collection('devices');
const queries: Promise<FirebaseFirestore.QuerySnapshot>[] = team.members
.filter(f => f.id !== userId)
.map(member => {
return devicesRef.where('userId', '==', member.id).get();
});
return Promise.all(queries)
.then((querySnapshots) => {
const tokens = [];
querySnapshots.forEach(snapShot => {
if (snapShot) {
snapShot.docs.forEach(doc => {
if (doc) {
const token = doc.data().token;
if (token) {
tokens.push(token);
}
}
})
}
});
if (tokens.length === 0) {
return Promise.resolve(null);
} else {
return admin.messaging().sendToDevice(tokens, payload);
}
})
.catch(err => {
console.error(err);
return Promise.resolve(null);
});
});
You can modify the above to suit your needs. Hope it helps
Upvotes: 5