1
1

More than 1 year has passed since last update.

Web Push機能のサンプルアプリをFCMで実装してみた

Last updated at Posted at 2022-03-16

はじめに

Mobile PushにFCMを使うことになったので、モバイルの知識がなくても出来るWeb Pushから始めることに。
ただ、執筆時点で推奨されているHTTP v1 APIでの使用にちょっとだけ苦労しました。
備忘録の意味も込めて手順を残しておきます。

FCM(Firebase Cloud Messaging)とは

今回はサンプルアプリの構築手順がメインですので、FCM自体の詳細な説明については参考リンクの記事に譲ります。
概要だけ書くと、FCMはFirebaseが提供するメッセージングサービスで、Android、iOS、WebブラウザなどにPush通知を飛ばすことができるようになります。
また、FCM APIへHTTPリクエストする際に事前登録したクライアントのトークンを指定することで、宛先を限定したPush通知が可能です。

Web Pushを試してみよう

今回のサンプルアプリ作成に使用した環境はこちら。

  • Googleアカウント(Firebaseサービス利用のため)
  • MacOS Monterey(v12.2.1)
  • Webブラウザ(Chrome 99.0.4844.51)
  • Node(v16.13.0)

Firebaseのプロジェクトを準備する

まずはFirebaseでFCMプロジェクトを作成。
Googleアカウントを使用してFirebaseにログインしたら、トップ画面でプロジェクトを作成。
image.png
プロジェクト名を入力すると、自動で一意のプロジェクトIDが割り当てられます。
この後Google Analyticsの有効/無効を任意に選択し、プロジェクトの作成を実行。
image.png
作成したプロジェクトの概要ページで</>を選択し、Web Pushを使用するためのアプリを追加します。
image.png
アプリのニックネームを適当に設定。
image.png
SDKの追加方法で<script>タグを使用するを選択するとconfig情報(firebaseConfig)が表示されるので控えておきましょう。
図1.png
Push通知に必要な秘密鍵は、プロジェクトの概要の右にある⚙アイコンからプロジェクトの設定サービス アカウントタブと進み、Node.jsを選択した状態で新しい秘密鍵の生成でDLしておきます。
ランダム文字列でファイル名が生成されますが、分かりやすいようここではservice-account.jsonとしておきます。
image.png
またCloud Messagingタブのウェブ設定にあるGenerate key pairでGCPサービス外へのPush通知に使用するVAPIDを生成し、こちらも控えておきましょう。
image.png

FCMからクライアントへトークンを発行する

FCMからクライアントのトークンを取得するためのサンプルアプリを実装します。
公式のサンプルをCloneし、下のようにソースを適宜書き換えます。(差分のみ記載)
なお、MY_…は先ほど控えた設定値に書き換えます。
firebase-messaging-sw.jsService Workerと言い、クライアントがバックグラウンドの際に実行される処理を定義します。
今回はサンプルのonBackgroundMessageをそのまま使用し、バックグラウンドの時にはPush通知がOS固有の通知センターに表示されるようにします。

messaging/index.html
- <script src="/__/firebase/9.6.7/firebase-app-compat.js"></script>
- <script src="/__/firebase/9.6.7/firebase-messaging-compat.js"></script>
- <script src="/__/firebase/init.js"></script>
+ <script src="https://www.gstatic.com/firebasejs/9.6.8/firebase-app-compat.js"></script>
+ <script src="https://www.gstatic.com/firebasejs/9.6.8/firebase-firestore-compat.js"></script>
+ <script src="https://www.gstatic.com/firebasejs/9.6.8/firebase-auth-compat.js"></script>
+ <script src="https://www.gstatic.com/firebasejs/9.6.8/firebase-messaging-compat.js"></script>
:
:
<script>
+ firebase.initializeApp({
+   apiKey: "MY_API_KEY",
+   authDomain: "MY_AUTH_DOMAIN",
+   projectId: "MY_PROJECT_ID",
+   storageBucket: "MY_STORAGE_BUCKET",
+   messagingSenderId: "MY_MESSAGING_SENDER_ID",
+   appId: "MY_APP_ID",
+   measurementId: "MY_MEASUREMENT_ID",
+ });
:
:
function resetUI() {
  clearMessages();
  showToken("loading...");
  // Get registration token. Initially this makes a network call, once retrieved
  // subsequent calls to getToken will return from cache.
    messaging
-      .getToken({ vapidKey: "<YOUR_PUBLIC_VAPID_KEY_HERE>" })
+      .getToken({ vapidKey: "MY_PUBLIC_VAPID_KEY_HERE" })
messaging/firebase-messaging-sw.js
- importScripts("/__/firebase/9.2.0/firebase-app-compat.js");
- importScripts("/__/firebase/9.2.0/firebase-messaging-compat.js");
- importScripts("/__/firebase/init.js");
+ importScripts("https://www.gstatic.com/firebasejs/9.6.8/firebase-app-compat.js");
+ importScripts("https://www.gstatic.com/firebasejs/9.6.8/firebase-messaging-compat.js");
:
:
+ firebase.initializeApp({
+   apiKey: "MY_API_KEY",
+   authDomain: "MY_AUTH_DOMAIN",
+   projectId: "MY_PROJECT_ID",
+   storageBucket: "MY_STORAGE_BUCKET",
+   messagingSenderId: "MY_MESSAGING_SENDER_ID",
+   appId: "MY_APP_ID",
+   measurementId: "MY_MEASUREMENT_ID",
+ });

ローカルサーバを立ち上げてindex.htmlにアクセスすると、トークン取得用の簡易アプリが表示されます。
しばらくするとFCMに登録したトークンが表示されるはずです。
このトークンも後で使用するので控えておきましょう。
図1.png

Push通知を送信する

Push通知を送信するための仮想サーバプログラムを実装します。
先ほどDLしたservice-account.jsonはこのサーバプログラムのルート直下に置いておきます。
MY_REGISTER_TOKENは先ほど控えたクライアントの登録トークン、YOUR_APP_IDはFCMのプロジェクトIDに書き換えます。

postPushMessage.js
var google = require("googleapis");
var https = require("https");

function getAccessToken() {
  return new Promise(function (resolve, reject) {
    const key = require("./service-account.json");
    const jwtClient = new google.Auth.JWT(
      key.client_email,
      null,
      key.private_key,
      ["https://www.googleapis.com/auth/firebase.messaging"],
      null
    );
    jwtClient.authorize(function (err, tokens) {
      if (err) {
        reject(err);
        return;
      }
      postPushMessage(tokens.access_token);
    });
  });
}

function postPushMessage(accessToken) {
  const data = JSON.stringify({
    message: {
      token:
        "MY_REGISTER_TOKEN",
      notification: {
        title: "フォアグラウンド通知のタイトルだよ。",
        body: "フォアグラウンド通知の本文だよ。",
      },
    },
  });
  const options = {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer " + accessToken,
    },
    body: data,
  };
  const url =
    "https://fcm.googleapis.com/v1/projects/YOUR_APP_ID/messages:send";
  const request = https.request(url, options, (response) => {
    console.log(`statusCode: ${response.statusCode}`);
  });
  request.write(data);
  request.end();
}

getAccessToken();

これで準備完了です。
実行してリクエストが成功することを確認します。

$ node ./postPushMessage.js
statusCode: 200

挙動確認

スクリプトを実行した際にブラウザがフォアグラウンドかバックグラウンドかで挙動が変わります。
フォアグラウンドの時には通知を受け取った旨がサンプルアプリに表示されます。
図1.png
バックグラウンドの時には通知センターに通知が表示されます。
image.png
お疲れ様でした。

旧HTTP APIと何が変わったの?

冒頭でも言いましたが、通知メッセージについては旧来のHTTP APIと現行のHTTP v1 APIで色々と仕様が異なっており、公式ドキュメントで差分を見ながら手探りで実装する必要がありました。
特に以下の違いを見つけるのに少々時間がかかってしまいました。

  • Push通知リクエストの向き先エンドポイントが異なる
旧仕様
POST https://fcm.googleapis.com/fcm/send
新仕様
POST https://fcm.googleapis.com/v1/projects/YOUR_APP_ID/messages:send
  • Push通知リクエスト用の認証には生成後不変なサーバーキーではなく一時的なトークンを使用する
  • Push通知リクエスト用のBodyも書き方が異なる
旧仕様
{
  "to": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
  "notification": {
    "title": "Portugal vs. Denmark",
    "body": "great match!"
  },
}
新仕様
{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification":{
      "title":"Portugal vs. Denmark",
      "body":"great match!"
    },
  }
}

参考になれば幸いです。

参考リンク

1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1