Nuxt+Firebase Cloud Messaging(FCM)でWebプッシュ通知を送る

WebでもFCMが使えるようになったので、試してみたときの備忘録。
これでSafari以外には、通知が送れるようになる(´ω`)

使い方

構成は、@nuxtjs/pwaでPWA化している感じ。

コンソール側

Settingsでウェブプッシュ証明書を作成して、鍵ペアを取得する。

スクリーンショット_2020-05-24_17_06_33.png

これを、PUBLIC_VAPID_KEYという環境変数に設定しておく。

クライアント側

firebaseの初期化

~/plugins/firebase.tsというファイルを用意し、firebaseを初期化

// ~/plugins/firebase.ts
import * as firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";
import "firebase/messaging";


if (!firebase.apps.length) {
  // まずは、firebaseの初期化
  firebase.initializeApp({
    apiKey: process.env.API_KEY,
    authDomain: process.env.AUTH_DOMAIN,
    databaseURL: process.env.DATABASE_URL,
    projectId: process.env.PROJECT_ID,
    storageBucket: process.env.STORAGE_BUCKET,
    messagingSenderId: process.env.MESSAGING_SENDER_ID,
    appId: process.env.APP_ID,
    measurementId: process.env.MEASUREMENT_ID
  });

  // Push通知をサポートしているかをチェック
  // サポートしていないと、firebase.messaging()を呼んだときに例外が発生
  const isSupported = firebase.messaging.isSupported();

  // コンソールで発行した、ウェブプッシュ証明書の鍵ペアを取得
  const publicVapidKey = process.env.PUBLIC_VAPID_KEY;
  if (!!publicVapidKey && process.client && !!isSupported) {
    // FCMの初期化。鍵ペアを設定する
    const messaging = firebase.messaging();
    messaging.usePublicVapidKey(publicVapidKey);

    // @nuxtjs/pwaが生成するsw.jsと、
    // 後で作成するFCM受信処理用のsw-firebase-messaging.jsを統合するための設定
    navigator.serviceWorker
      .register("/sw.js")
      .then(registration => messaging.useServiceWorker(registration))
      .catch(err => console.error(err));
  }
}

export default firebase;
トークンの取得

Push通知を送る際の宛先として、トークンを指定しないといけないので取得。
トークンは端末ごとに取得する必要があるので、注意が必要。
※PCとスマホだとそれぞれトークンが違う

import firebase from "~/plugins/firebase";

// トークンの取得
public async getToken(user: User) {
  const isSupported = firebase.messaging.isSupported();
  if (!isSupported) return;
  const token = await firebase.messaging().getToken();

  // firestoreにトークンを保存しておく処理(中身は略)
  await saveToken(user, token);
}

Firestoreなどへユーザごとにトークンを保存しておく。

firebase.messaging().getToken();を呼んだ際に、
通知の設定が「確認」だと、許可を求めるダイアログが表示される。

スクリーンショット 2020-05-24 16.30.23.png

これが許可されていないと、トークンも取得できない。

スクリーンショット 2020-05-24 16.30.35.png

メッセージを受け取ったときの処理

メッセージを受信したときに受け取る関数は2つあり、

  • フォアグラウンド(画面を見ている時) ... onMessage
  • バックグラウンド(画面を見ていない時) ... setBackgroundMessageHandler

・【参考】JavaScript クライアントでメッセージを受信する  |  Firebase

今回はバックグラウンドのときに通知を送りたいので、
setBackgroundMessageHandlerを設定していく。

こんな感じ。

ドキュメントを見ると、『firebase-messaging-sw.jsというファイル名で作成』と書かれているけど、
その名前にすると、自動で読み込まれてしまう。。

開発用と本番用で切り替えたいときなどもあるので、@nuxtjs/pwaが生成するsw.jsと統合できるように、
sw-firebase-messaging.jsという名前でファイルを作成しておく。

ファイル名を変更したので、上で書いている「firebaseの初期化」の部分で、
messaging.useServiceWorker(registration)を呼んでいる形。

// ~/static/sw-firebase-messaging.js
importScripts("https://www.gstatic.com/firebasejs/7.14.2/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/7.14.2/firebase-messaging.js");

// Firebaseの初期化
firebase.initializeApp({
  apiKey: "...",
  authDomain: "...",
  databaseURL: "...",
  projectId: "...",
  storageBucket: "...",
  messagingSenderId: "...",
  appId: "...",
  measurementId: "...",
});

// [START background_handler]
const isSupported = firebase.messaging.isSupported();
if (!!isSupported) {
  const messaging = firebase.messaging();
  // バックグラウンド時の処理
  messaging.setBackgroundMessageHandler(function(payload) {
   // 受け取ったFCMの内容を取得
    const notificationTitle = payload.notification.title;
    const notificationOptions = {
      body: payload.notification.body,
      icon: payload.notification.icon,
    };

    // 通知を作成する
    return self.registration.showNotification(notificationTitle, notificationOptions);
  });
}
// [END background_handler]
nuxt.config.tsでPWA関連の設定をする

作成したsw-firebase-messaging.jsを取り込む設定と、
@nuxtjs/pwaが生成するmanifest.jsonに、gcm_sender_idを追加する設定を追加

import { Configuration } from "@nuxt/types";

const config: Configuration = {
  // 略
  modules: [
    "@nuxtjs/pwa",
  ],

  workbox: {
    // sw-firebase-messaging.jsをimportするように追加
    importScripts: [
      "sw-firebase-messaging.js"
    ]
  },

  pwa: {
    manifest: {
      // manifest.jsonにgcm_sender_idを追加
      gcm_sender_id: process.env.MESSAGING_SENDER_ID || ""
    }
  },
};

export default config;

これでクライアント側はOK!

サーバ側: メッセージを送信する

メッセージの送信は、firebase-adminでできる。
アプリサーバーからの送信リクエストを作成する  |  Firebase

firestoreを利用しているので、Cloud Functionsのfirestoreトリガーを使い、
ドキュメントが追加されたら通知するようにしている例。

import * as functions from "firebase-functions";
import admin from "../common/firebaseAdmin"; // 初期化済みのfirebase-admin

export default functions
  .firestore.document("ドキュメントのパス")
  .onCreate(async (snap, context) => {
    // getTokenで保存しておいたトークンを取得(中身は略)
    const token = getToken();

    // 通知の送信
    const title = "通知するタイトル";
    const body = "通知する本文";
    const icon = "通知で表示するアイコン画像のURL"
    const link = "通知をタップしたときに開くURL"
    await admin.messaging().send({
        // 送信先の端末のトークン
        token: token,
        // 通知する内容
        notification: {
          title: title,
          body: body
        },
        // Web Push向けの通知内容
        webpush: {
          notification: {
            icon: icon
          },
          fcmOptions: {
            link: link
          }
        }
    });
  });

送信はこれだけ!

ほかの小ネタ

トークンを削除する

firebase.messaging().deleteToken();でトークンを無効化できる。

// トークンの削除
public async deleteToken(user: User) {
  const isSupported = firebase.messaging.isSupported();
  if (isSupported) {
    const token = await firebase.messaging().getToken();
    await firebase.messaging().deleteToken(token);
  }
}

フォアグラウンドで通知を受け取ったときになにかする

onMessageを使うと、通知を受け取ったときに呼び出してくれる。

firebase.messaging().onMessage(async (payload) => {
  // 受け取ったときの処理
});

トークンが変更されたときになにかする

onTokenRefreshを使うと、トークンが更新されたときに呼び出してくれる。
新しいトークンは再度getToken()を呼ばないといけない。

firebase.messaging().onTokenRefresh(async () => {
  // トークンが更新されたときの処理
});

通知の許可状態を確認する

通知の状態は、Notification.permissionで確認できるらしい。
Notification.permission - Web API | MDN

if (Notification.permission === "default") {
  // 確認(デフォルト)
} else if (Notification.permission === "granted") {
  // 許可
} else if (Notification.permission === "denied") {
  // 拒否
}

以上!!

【PR】これをつかって、こんなのつくりました!

スクリーンショット 2020-05-24 12.44.28.png

こんな通知を受け取れます!
スクリーンショット_2020-05-24_13_34_17.png

1週間でWebサービスを作るイベント web1weekへの投稿作品です!
よかったら、遊んでみてください(´ω`)

■エアで投げ銭できるWebサービス「エア銭」
URL: https://air-money.netlify.app/

参考にしたサイトさま

Originally published at qiita.com
ツイッターでシェア
みんなに共有、忘れないようにメモ

きらぷか@i18n補助ツール『トランスノート』開発者

フリーエンジニア/今はNuxt.js/いつかFlutter 受託&アプリ/Webサービス/ゲームを #個人開発 CS修士→SIer/R&D→フリー #paiza はAランクで満足/AtCoderしたい 仕事依頼やご相談はDMまで Kotlin/Python/Swift/Unity/Java/Haskell/DDD

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!
ボードとは?

関連記事

コメント