Cloud FunctionsでFirestoreの更新を特定のデバイスにpush通知する


Cloud Functions × Firestore × Firebase Cloud Messaging

Cloud Functionsはサーバレスコンピューティングサービスです。様々なイベントをトリガーにしてアクションを実行することができ非常にコストパフォーマンスに優れています。

今回はこのCloud Functionsを用いてFirestoreにデータが追加されたら特定のデバイスにpush通知が届くようにします。

Structure

通知を送信したいDBを設計します。今回は簡略化のためにnotificationsというコレクションにしています。
ここであらかじめテーブルにデバイスのidが紐付いているとやりやすいと思います。

notifications
│
├── notification
│   ├── text
│   └── users
.        ├── bJi8iJLw0ufF1HCO17v3b7UTVS53
.        .
.        .

Architecture

Steps

以下のことが既に完了している前提で実装していきます。多分この記事にたどり着くような人はコーディングで行き詰まった人だと思います。Firestoreのサンプルがここしかなかった。。。

  • Firebaseプロジェクトは作成済み
  • 必要なパッケージはインストール済み
  • クライアント側のリモート通知設定済み

1. Cloud Functionsをスタートする

まずプロジェクトを作成します

$ Firebase init


functionsディレクトリ内にあるindex.jsにコードを記述していきます。

2. コードを記述

index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

exports.sendNotifications = functions.firestore.document('notifications/{notificationId}').onCreate(
    async (snapshot) => {
        // Notification details.
        const newValue = snapshot.data();

        const payload = {
            notification: {
                title:"新規ライブ",
                body: newValue.text + "の新しいライブが追加されました!",
                content_available: 'true',
                sound: "default",
                click_action: `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com`,
            }
        };

        const userIDs = newValue.users

        // Get the list of device tokens.
        userIDs.forEach(function (value) {
            admin.firestore().collection('fcmTokens').doc(value).get()
                .then(function (querySnapshot) {

                    let fcmToken = querySnapshot.data().fcmToken
                    admin.messaging().sendToDevice(fcmToken, payload);
                    return;
                }).catch(error => { return });
        });
    });

なんやこれポイント

サンプルに const allTokens = await admin.firestore().collection('fcmTokens').get(); ってあったのでてっきりFirestoreのfcmToken勝手にここに格納されてるのかと思いきや自分でDBつくるっぽかった。なので、userIdと紐付けて識別できるようにしました。

fcmTokens
│
├── fcmToken(id: uid)
│   └── fcmToken
.       
.    

まずfunctions.firestore.document()の引数にコレクションを指定します。今回はテーブルに新たなドキュメントが追加されたときに発火するようにしたいのでonCreate()を呼び出しています。
その他にもonUpdate()onDelete()などさまざまなトリガーがあります。詳しくはこちら

そして、payloadに通知の設定をします。

fcmTokensというコレクションからfcmtokenをゲットすることができます。RealtimeDBより便利な気がする。

最後にsendToDevice()で取得したユーザのトークンにまとめてpush通知を送信しています。

3. Deploy

$ firebase deploy

なんか死んでたらログを確認してちょこちょこ直していきましょう。

4. Test

ためしにPythonからFirestoreに新しいドキュメントを追加してみます

import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore


cred = credentials.Certificate()
firebase_admin.initialize_app()
db = firestore.client()

notification_data = {
    "text": "MY FIRST STORY",
    "users": ["gHrdAxQhFYdTmoFjgi8RX8PljXv1"]
}

db.collection(u'notifications').document().set(notification_data)


任意のデバイスにpush通知が来ればオッケー。たっくーTV見てたのは内緒。

コードはこちら