Cloud Functions、便利ですよね。
こんにちは、株式会社Playground Webシステム開発部の稲垣です!
前回はCloud Functionsに色々な処理を任せてしまおうという話をしました。
今回は、ほとんどどのアプリでも実装することになるであろう「Push通知」について解説します。
FCMトークンの設定や受信時の処理はモバイル側で実装する必要がありますが、「送信」についてはCloud Functionsで実装できます。
今回実装したい関数は以下のような要件と仮定します。
- アプリ管理者がWebの管理画面から配信したいお知らせを登録する
- お知らせ登録時にユーザーにプッシュ通知で配信される
- 画像や通知音付きのリッチプッシュにしたい
目次
Cloud Functionsでリッチプッシュを送信する関数を作ろう
実際にPlaygroundで使っているコードを例にしながら解説していきます。
プッシュ通知を送信するトリガーの設定
「アプリ管理者によるニュース登録」をトリガーとしたいので、newsモデル配下にデータが作成された時に、Cloud Functionsが起動するように設定する。
該当のコードは以下の通りです。
functions/src/index.ts
export const sendNotification = functions.database.ref("/news/{newsId}")
.onCreate(async (snapshot: any, context: any) => {
Cloud Functionsからモバイルデバイスに送信するデータの作成
次に、Push通知で送信するデータを作ります。
FirebaseのFCMメッセージは2種類のデータを格納できます。
- notification(通知メッセージ)
- data(データメッセージ)
notificationには事前に定義された値を格納できます。
一方、dataは開発者が自由に値を設定できるオブジェクトです。
詳細はFirebaseの公式ドキュメントを参照してください。
弊社ではPush通知=「バッジ+画像+通知音付きのリッチプッシュ」であることが多いので、その場合の具体例を以下に示します。
functions/src/index.ts
const value = snapshot.val(); //新規作成されたニュースの値を格納
const message = {
notification: {
title: value.subject, // 通知のタイトル
body: value.description, // 通知の本文
sound: "default", // 受信時の通知音
mutable_content: 'true', // 画像付きのリッチプッシュに必要
content_available: 'true' // アプリがバックグラウンドでも通知を届けるために必要
},
data: {
"image-url": value.image_url // Cloud Storageに格納している画像ファイルのURL
}
};
Cloud Functionsからモバイルにリッチプッシュを送信するメソッドの使い方
Cloud FunctionsからモバイルにPush通知を送る時は、Firebase Admin SDKのメッセージを使います。
Push通知を一斉配信するために、sendAll()やsendMulticast()が使えたら便利なのですが、これらのメソッドが受け取れる値はMessage型だけなんですよね。
Message型はnotificationにsoundやmutable_contentといった値を設定できません。
つまり、リッチプッシュを実装できないということです。
(iOS/Androidアプリ側でこねくり回せばできるのかもしれません)
notificationにsoundなどの値を設定できるのは、MessagingPayload型だけ。
そしてMessagingPayload型を渡せるのはSendToXXXXX()メソッドのみです。
ということで仕方なくここではSendToDevice()を使います。
実際にメッセージを送信するための関数は以下のように実装しました。
functions/src/index.ts
const sendMessage = (fcmToken: string, message: any, options: any) => {
return admin.messaging().sendToDevice(fcmToken, message, options)
.then(() => {
return Promise.resolve(null)
})
.catch(() => {
return Promise.reject(null)
});
};
この関数はPromiseを返します。
あとでPromise.all()を使って配信を並列処理にするためです。
Cloud Functionsからリッチプッシュ通知を一斉配信する方法
Push通知を一斉配信する箇所の処理の流れは以下の通りです。
- userモデルから取得したデータからFCMトークンを取り出す
- FCMトークンが登録されているユーザーなら、promisesという配列にsendMessage()を引数と共に追加する
- 1〜2を全ユーザー分繰り返したら、最後にPromise.all(promises)で並列実行する。
実際のコードは以下のようになります。
functions/src/index.ts
const users: any = await queryUserData(); // Userモデルからデータを取得
const userModelLength = users.numChildren(); // Userモデルのデータ数を取得
let promises: Array = [],
counter = 0;
users.forEach((user: any) => {
const userValue = user.val();
const fcmToken: string = (userValue.fcmToken) ? userValue.fcmToken : "";
if (fcmToken !== "") {
promises.push(sendMessage(fcmToken, message, options))
}
counter += 1
// 全ユーザーの繰り返し処理完了後にPromise.all()で実行する
if (counter === userModelLength) { return Promise.all(promises) }
})
Cloud Functionsからモバイルにリッチプッシュ通知を送信するために気をつけること
このFunctionを実装する際にハマった点があるので共有します。
- FCMトークンが登録されていないユーザーには送信できないので、例外処理や条件分岐が必要
- soundやmutable_contentなどを設定したい時はsendメソッドは使えないのでSendToメソッドを使う
- Promise.all()で並列処理にしないと、ユーザー数が多い場合、配信時間にタイムラグがでる
- ただし並列処理にする場合、Cloud Functionsのメモリ上限を気にしなきゃいけない
以上です。
Push通知が実装できると一気にアプリでできる幅が広がります。
トリガーを工夫すれば、色々なタイミングでPush通知を送ることができますよ。
- 無料・簡単・片手でホームページを作成できる自社サービス Rakwi
- Web制作とアプリ開発を学べるオンラインプログラミング講座 Upstairs
- 開発,DX推進支援サービス スタートアッププラン
ちょうど調べていて試していたのですが、Messageでもこれで行けましたよ!
https://firebase.google.com/docs/reference/admin/node/admin.messaging.Aps.html#mutablecontent
コメント有り難うございます!とても参考になります。
contentAvailableやcontentMutableというプロパティが有効なんですね。
ドキュメントをしっかり読み込まないとですね(^^;)