#やりたいこと
アプリからメッセージを送信→DB(Firestore)にメッセージが保存される→それを監視していたCloudFunctionsがプッシュ通知を送る
関心事が発生したときにユーザーに通知する
これと同じようなイメージ。
#Firestore設計
chatrooms(コレクション)
∟chatroom1(ドキュメント)
∟messages(コレクション)
∟message1(ドキュメント)
∟message2(ドキュメント)
∟message3(ドキュメント)
∟chatroom2(ドキュメント)
∟messages(コレクション)
∟message1(ドキュメント)
∟message2(ドキュメント)
:
:
- ルートレベルにchatroomsコレクションがある
- chatroomsドキュメント配下にmessagesサブコレクションがある
このように設計しました。
#アプリ設計
設計というほどではないのですが、以下のように事前にトピックを購読させます。
タイミングとしてはLINEでいうところの「グループチャットに参加する」のようなものを契機にすればよいのではないでしょうか。
その際トピック名としてチャットルームのドキュメントIDを指定します。
Messaging.messaging().subscribe(toTopic: "/topics/\(chatroomドキュメントのid)")
こうすることで/topics/{chatroomドキュメントのid}
宛てにFCMメッセージが送られた際にプッシュ通知を受け取れるようになります。
#CloudFunctionsの設計
CLIツールをインストールしていない場合はインストールしてください。
$ npm install -g firebase-tools
適当にディレクトリを作ってそこでプロジェクトを初期化します。
$ firebase init functions
プロジェクトを選択し、JavascriptかTypescriptを選択します。
今回はJavascriptを選択した前提で話を進めます。
そのあと色々聞かれますが全てEnterでOKです。
index.jsに以下を追記します。(公式の手順通りですが)
これで
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
これでFirestoreにアクセスできるようになります。
続いて肝心のプッシュ通知を行う関数を定義します。
Firestoreへの追加をトリガーにプッシュ通知を送っています。
こちらを参考にしました。
Cloud Functions による Cloud Firestore の拡張
exports.sendNotifications = functions.firestore
.document('/chatrooms/{chatroomId}/messages/{messageId}')
.onCreate((event, snapshot) => { // 公式ではeventとなっていたが、そうするとevent.params.chatroomIdのように値を取ってこれなかった。
var topic = snapshot.params.chatroomId; // こうすることで上で指定したドキュメントのパスの{chatroomId}部分が取得できる。
var payload = {
notification: {
title: "新着メッセージ", // この辺りを動的に取ってくるケースが多いかな?
body: "新着メッセージがあります"
}
};
var options = {
priority: "high"
};
// 指定したtopic宛てにメッセージを送る
return admin.messaging().sendToTopic(topic, payload, options)
.then(function(response) {
return console.log("Successfully sent message:", response);
})
.catch(function(error) {
return console.log("Error sending message:", error);
});
});
そしてデプロイします。
$ firebase deploy --only functions
上記のコードであればエラーは出ないと思いますが、当初自分はsendToTopic内のconsole.logにreturnをつけていなかったので以下のようにLintで怒られました。
Each then() should return a value or throw promise/always-return
うまくデプロイが済めばこうなるはずです。
✔ functions: Finished running predeploy script.
i functions: ensuring necessary APIs are enabled...
✔ functions: all necessary APIs are enabled
i functions: preparing functions directory for uploading...
i functions: packaged functions (49.67 KB) for uploading
✔ functions: functions folder uploaded successfully
i functions: creating function sendNotifications...
✔ functions[generateThumbnail]: Successful delete operation.
✔ functions[sendNotifications]: Successful create operation.
✔ Deploy complete!
そしてアプリからメッセージを送信します。(当記事ではメッセージ送信箇所について言及していませんが、Firestoreのmessageドキュメントを生成する処理を実行すればOKです。アプリでそれを実装するのが面倒な場合はFirestoreのコンソールから直接messagesドキュメントを作っても動作確認は可能です。)
すると、そのチャットルームを購読している端末に通知がいくと思います。
Cloud Functionsのログにも結果が出力されているはずです。
もっといい方法があれば教えていただきたいです。