はじめに
今記事はCloud Function for Firebaseを使って、チーム(LINEのチャット的なやつ)にコメントがきたらチームの参加者にプッシュ通知を打つという実装を行った時のメモです。
通知の設定などは今記事では触れず、Firebase Realtime Databaseにコメントが追加されたことをトリガーにしてFirebase Cloud Messagingに通知を打つことを命令する部分のコードを紹介します。
コードは2017年8月のものです。
Cloud Function for Firebaseとは
以下の記事を参考にしてsetupしました。
Cloud Functions for Firebaseとは?
っていうか、そもそもnpmってなんぞや?って感じだったのでnode.jsをインストールするところから。
ってことで、v8.3.0
をインストール。
index.jsができたら準備完了。
var functions = require('firebase-functions');
exports.helloWorld = functions.database.ref('/teams')
.onWrite(event => {
const val = event.data.val();
console.log('helloWorld', val);
})
↑Firebase Realtime Databaseの/teams
にデータが書き込まれたら発火してHelloWorldが出るやつです。
実際に書いたnode.js
先に書いた Hello World
が以下になりました。
const functions = require('firebase-functions');
const admin = require('firebase-admin');
var teamRef
admin.initializeApp(functions.config().firebase);
exports.commentPush = functions.database.ref('/teams/{teamId}/comments/{commentId}')
.onWrite(event => {
const item = event.data;
const userName = item.child("userName").val();
const commenterUid = item.child("uid").val();
const comment = item.child("comment").val();
teamRef = item.ref.parent.parent
teamRef.once('value').then(function(snapshot) {
const teamName = snapshot.val().teamName;
// 通知のJSON
const payload = {
notification: {
title: teamName,
body: userName + "さん:" + comment,
badge: "1",
sound:"default",
}
};
// 通知を打つ対象を検索してそのtokenにたいして打つ
const members = snapshot.val().members
console.log("Successfully fetch members:", members);
for (key in members) {
console.log("Successfully fetch member:", members[key]);
const uid = members[key].uid
const name = members[key].name
console.log("uid:", uid);
// コメントした本人には通知を打たない
if (uid == commenterUid) {
console.log("本人には通知を打たない");
continue;
}
// fcmTokenを取得
getTargetFcmToken(uid, function(token){
if (token == null) {
// 通知OFFのユーザーには通知を打たない
console.log(name, "に通知を打たない");
return;
}
console.log("fcmToken:", token);
// tokenが欲しい
pushToDevice(token, payload);
});
};
});
});
// TODO: uidを使ってuserのdatabaseを検索
var getTargetFcmToken = function(uid, callback){
console.log("getTargetFcmToken:");
const rootRef = teamRef.parent.parent;
const userRef = rootRef.child("users").child(uid);
userRef.once('value').then(function(snapshot) {
const isOn = snapshot.val().commentPush;
console.log("isOn:", isOn);
if (isOn == false) {
// 通知設定がOFFの場合
console.log("return callback null");
callback(null);
return
}
const fcmToken = snapshot.val().fcmToken
console.log("return callback fcmToken", fcmToken);
callback(fcmToken);
});
}
// 特定のfcmTokenに対してプッシュ通知を打つ
function pushToDevice(token, payload){
console.log("pushToDevice:", token);
// priorityをhighにしとくと通知打つのが早くなる
const options = {
priority: "high",
};
admin.messaging().sendToDevice(token, payload, options)
.then(pushResponse => {
console.log("Successfully sent message:", pushResponse);
})
.catch(error => {
console.log("Error sending message:", error);
});
}
これで、チームにコメントがあったときに、そのチームに参加している自分以外の人に対してプッシュ通知が打てました。以下で詳しく解説していきます。
通知を打つフロー
Firebase Realtime Databaseに値が書き込まれる
まずは、Firebase Realtime Databaseのteams/{id}/comments
に値が書き込まれます。
teams/{id}/commentsの様子
teams/{id}/membersの様子
teamのRefが値の書き込みイベントから取れるので、チームに参加している人のuidを取得します。
// 通知を打つ対象を検索してそのtokenにたいして打つ
const members = snapshot.val().members
console.log("Successfully fetch members:", members);
for (key in members) {
console.log("Successfully fetch member:", members[key]);
const uid = members[key].uid
const name = members[key].name
console.log("uid:", uid);
// コメントした本人には通知を打たない
if (uid == commenterUid) {
console.log("本人には通知を打たない");
continue;
}
// fcmTokenを取得 (このメソッドは次のステップで定義)
getTargetFcmToken(uid, function(token){
if (token == null) {
// 通知OFFのユーザーには通知を打たない
console.log(name, "に通知を打たない");
return;
}
console.log("fcmToken:", token);
// tokenに対して通知を打つ (このメソッドは次の次のステップで定義)
pushToDevice(token, payload);
});
};
user検索してfcmTokenを取り出す
uidを使ってFirebase Realtime Database内のuserを検索し、そのuserのfcmTokenを取得します。
// TODO: uidを使ってuserのdatabaseを検索
var getTargetFcmToken = function(uid, callback){
console.log("getTargetFcmToken:");
const rootRef = teamRef.parent.parent;
const userRef = rootRef.child("users").child(uid);
userRef.once('value').then(function(snapshot) {
const isOn = snapshot.val().commentPush;
console.log("isOn:", isOn);
if (isOn == false) {
// 通知設定がOFFの場合
console.log("return callback null");
callback(null);
return
}
const fcmToken = snapshot.val().fcmToken
console.log("return callback fcmToken", fcmToken);
callback(fcmToken);
});
}
通知を打つ
fcmTokenを手に入れたのであとは、それに対して打つだけです。Firebase Cloud Messagingで打つためのメソッドが用意されています。
// 特定のfcmTokenに対してプッシュ通知を打つ
function pushToDevice(token, payload){
console.log("pushToDevice:", token);
// priorityをhighにしとくと通知打つのが早くなるかも
const options = {
priority: "high",
};
admin.messaging().sendToDevice(token, payload, options)
.then(pushResponse => {
console.log("Successfully sent message:", pushResponse);
})
.catch(error => {
console.log("Error sending message:", error);
});
}
完成。
まとめ
- iOS側で発行されたFirebase Cloud MessagingのfcmTokenをFirebase Realtime Databaseのuserに保存
- Firebase Cloud FunctionではuidをkeyにUserを検索し、fcmTokenを取り出す。
- そのfcmTokenに対してFirebase Cloud Messagingを使って通知打つ
追記
TypeScriptで、同様にして、Firestoreに値が書き込まれたらFCMで通知を打つサンプルコードを公開しました。