1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FCM (Firebase Cloud Messaging) を使ってWebプッシュ通知を実装してみる

Posted at

はじめに

今更感はありますが、今までWebプッシュ通知を実装した事がなかったので、Firebase Cloud Messagingを使って実装してみました。

Firebase Cloud Messaging(FCM)は、Googleが提供するプッシュ通知サービスです。

https://firebase.google.com/docs/cloud-messaging?hl=ja

プッシュ通知とは?

プッシュ通知は、iOS、Android、Webなどのデバイスからユーザーに対して通知する機能です。プッシュ通知の種類は、大きく2つに分けられます。

種類 説明
A2P
(Application to Person)
アプリケーションからユーザーへのプッシュ通知 ・ニュースアプリの最新記事通知
・ECサイトのセール通知
・天気予報の警報通知
P2P
(Person to Person)
ユーザーからユーザーへのプッシュ通知 ・メッセージアプリの新着通知
・SNSの投稿通知
・チャットの返信通知

プッシュ通知の仕組み

プッシュ通知の仕組みは、任意のトリガーによりサーバーからクライアントアプリにプッシュ通知送信します。

アクター 説明
ユーザー アプリケーションを利用するエンドユーザー
クライアントアプリ ユーザーが使用するアプリケーション
(iOS/AndroidアプリやWebアプリ)
プッシュ通知サービス プッシュ通知の配信を管理するサービス
(Firebase Cloud Messagingなどのサービス)
バックエンド アプリケーションのサーバーサイドシステム

ユーザーに対して通知の許可が必要

ブラウザでサイトを開いていると、下記のようなポップアップの表示されることがあります。Webアプリでプッシュ通知を表示しようとすると、ブラウザを介してユーザーの許可をもらう必要があります。

image.png

トークンについて

FCMが返すトークンは、ユーザーのデバイスやブラウザを一意に識別するためのものです。
実際にプッシュ通知を送信する際の送り先として利用します。

トークンを取得する際は、VAPID(Voluntary Application Server Identification)という公開鍵と秘密鍵のペアを使用した認証を通す必要があります。

Webプッシュ通知の認証に使用されるです。公開鍵を取得する際にクライアントからFCMに送付し、認証を行います。認証が通るとトークンが発行されます。

ブラウザがバックグラウンド時の動作について

ブラウザがバックグラウンド時は、ServiceWorkerを使用して、プッシュ通知を受信します。

フォアグラウンド/バックグラウンドの補足

  • フォアグラウンド:ブラウザのタブがアクティブな状態
  • バックグラウンド:ブラウザのタブが非アクティブな状態、またはブラウザが最小化されている状態

前提

JavaScriptは、シングルスレッドで実行される言語となります。

WebWorkerについて

WebWokerは、メインスレッドとは別スレッドでJavaScriptを動作させることができます。
なので、重たい処理などでメインスレッドに影響ある場合は、WebWokerに重たい処理を逃がすなどの使い方になります。

参考:https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API

ServiceWorkerについて

ServiceWorkerは、WebWokerの中の1種類にあたります。ブラウザを閉じてもバックグラウンドでスレッドを常駐することができます。
ブラウザをアクティブでないときでもプッシュ通知を受け取りたい場合は必要な機能になります。

参考:https://developer.mozilla.org/ja/docs/Web/API/ServiceWorker

試してみる

実際にWebプッシュ通知を実装してみます。

試す内容

今回は、プッシュ通知を受けたら、下記の表示を行うように実装します。

  • フォアグラウンド時:トースト表示
  • バックグラウンド時:Windows通知表示

対象の通知先は以下とします。

  • Windows11のPC
  • Chromeブラウザ

Step1. Firebase Cloud Messagingの設定

準備:Firebaseにプロジェクトを作成する
  1. Firebaseのサイトにログインします
    https://firebase.google.com/?hl=ja

  2. 画面右上の「Go to console」を押します
    image.png

  3. 「Firebase プロジェクトを使ってみる」を押します
    image.png

  4. 「プロジェクト名」に任意の名前を入力します。「規約」などにチェックを入れ、画面右下の「続行」ボタンを押します
    image.png

  5. 途中、AIを有効にするかなど聞かれますが、任意の選択にして「プロジェクトを作成」ボタンを押します

  6. 少し待つとプロジェクトの準備ができるので、「続行」ボタンを押します
    image.png

  7. これで準備ができました
    image.png

Firebaseにアプリを設定する

  1. 歯車アイコンを押して、プロジェクトの設定画面を開きます
    image.png

  2. プロジェクトの設定画面で下までスクロールして、Webのアイコンを押します
    image.png

  3. 任意のニックネームを入力して、「アプリを登録」ボタンを押します
    image.png

  4. アプリへのSDKの組み込み方が表示されますが、そのまま進みます
    image.png

  5. ウェブアプリが追加されました
    image.png

FCMのキーペアを作成する

  1. プロジェクト設定から「Cloud Messaging」タブを押します
    image.png

  2. 下までスクロールして「GEnerate key pair」ボタンを押します
    image.png

  3. 作成できました。(キーペアは後ほど使います)
    image.png

Step2. Webアプリの実装

プロジェクトを作成する

1.Viteを使用し、 React と TypeScript のプロジェクトを作成します

npm create vite@latest

√ Project name: ... push_notification_app
√ Select a framework: » React
√ Select a variant: » TypeScript

2.必要な依存パッケージをインストールします

cd <プロジェクト名>
npm install

トークン取得処理を実装

Firebase Cloud Messagingとフロントエンドがやり取りするため、SDKをインストールします

npm install firebase

バックグラウンドの受信処理

Webブラウザのバックグラウンドでプッシュ通知を受けるため、ServiceWorkerを利用します。

./public/firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/10.13.2/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/10.13.2/firebase-messaging-compat.js');

// 本ファイルはビルドを通らないので、バックグラウンド用の設定が必要
firebase.initializeApp({
  apiKey: "API_KEY",
  authDomain: "push-notification-20a64.firebaseapp.com",
  projectId: "push-notification-20a64",
  storageBucket: "push-notification-20a64.firebasestorage.app",
  messagingSenderId: "417934552820",
  appId: "1:417934552820:web:71827d1886afd79b2678a1"
});

const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) => {
  // ブラウザがバックグラウンド中にプッシュ通知を受けると呼ばれる
  // Firebase SDKが自動でshowNotificationを実行する
  // https://developer.mozilla.org/ja/docs/Web/API/ServiceWorkerRegistration/showNotification
  console.log('[firebase-messaging-sw.js] Received background message ', payload);
});

firebase-messaging-sw.jsをルートに置けば登録処理は不要
firebase-messaging-sw.jsは、Firebase用のServiceWorkerファイルで、ルートフォルダに置くとSDKが自動的に読み取って、ServiceWorkerに登録してくれるので、登録処理は不要です。
今回は、publicフォルダに配置することで対応しています。

フォアグランドの受信処理

トースト表示に必要なライブラリをインストールします。

npm install @mui/material @emotion/react @emotion/styled

設定ファイルを新規作成して、FCMと接続するために必要な設定を記述します。

./src/config.ts
export const firebaseConfig = {
  apiKey: 'API_KEY',
  authDomain: 'PROJECT_ID.firebaseapp.com',
  databaseURL: 'https://PROJECT_ID.firebaseio.com',
  projectId: 'PROJECT_ID',
  storageBucket: 'PROJECT_ID.appspot.com',
  messagingSenderId: 'SENDER_ID',
  appId: 'APP_ID',
  measurementId: 'G-MEASUREMENT_ID'
};

export const vapidKey = '<YOUR_PUBLIC_VAPID_KEY_HERE>';

Viteのデフォルトコードをベースに、フォアグラウンドでプッシュ通知を受信した際の処理を記載します。

./src/App.tsx
import { useState, useEffect } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
+ import { type NotificationPayload } from 'firebase/messaging';
+ import { initializeApp } from "firebase/app";
+ import { getToken, getMessaging, onMessage } from "firebase/messaging";
+ import { firebaseConfig, vapiKey } from './config';
+ import { Snackbar, Alert, AlertTitle } from '@mui/material';

+ const app = initializeApp(firebaseConfig);
+ const messaging = getMessaging(app);

+ function requestPermission() {
+   console.log('Requesting permission...');
+   Notification.requestPermission()
+   .then((permission) => {
+     if (permission === 'granted') {
+       console.log('Notification permission granted.');
+     }
+   })
+ };

function App() {
  const [count, setCount] = useState(0);
+ const [message, setMessage] = useState<NotificationPayload | undefined>(undefined);
+ const [open, setOpen] = useState(false);

+ const handleClose = () => {
+   setOpen(false);
+ };

+ useEffect(() => {
+   getToken(messaging, {vapidKey: vapiKey})
+     .then((currentToken) => {
+       if (currentToken) {
+         console.log('トークンを取得しました。', currentToken);
+       } else {
+         console.log('トークンが利用できません。');
+         requestPermission();
+       }
+     })
+     .catch((err) => {
+       console.log('トークンの取得に失敗しました。', err);
+     });
+ }, []);

+ onMessage(messaging, (payload) => {
+   console.log('Message received. ', payload);
+   if (payload.notification) {
+     setMessage(payload.notification);
+     setOpen(true);
+   }
+ });

  return (
    <>
      <div>
        <a href="https://vite.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
+     <Snackbar 
+       open={open}
+       autoHideDuration={3000}
+       onClose={handleClose}
+       anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
+     >
+       <Alert onClose={handleClose} severity="info" sx={{ width: '100%' }}>
+         <AlertTitle>{message?.title}</AlertTitle>
+         {message?.body}
+       </Alert>
+     </Snackbar>
  </>
  )
}

export default App

3. ローカル環境でアプリケーションを起動してみます。

npm run dev

Step3. FCMからテストメッセージを送ってみる

  1. Firebaseのが画面からMessagingのメニューを開き、「最初のキャンペーン作成」ボタンを押す
    image.png

  2. ダイアログが表示されるので、Firebase Notification メッセージのまま、「作成」ボタンを押す
    image.png

  3. 任意の通知タイトルと通知テキストを入力して、「テストメッセージを送信」ボタンを押す
    image.png

  4. 今回実装したフロントエンドで取得したトークンを入力する
    image.png

image.png
(※ 今回は、取得したトークンをコンソールログに表示してたので、開発者ツールを開いてトークンを確認しました。)

フォアグラウンドの場合...

でました
image.png

バックグラウンドの場合...

でました (Windowsの通知として受信しました)
image.png

これでひとまずWindows11のChromeブラウザで表示することが出来るようになりました。

所感

今回、Firebase Cloud Messagingを使ってWebプッシュ通知を実装してみました。
よくある技術ではありますが、改め実装しようとすると、意外と難しいなというのが率直な感想です。
(ServiceWorkerとFirebaseのSDKの絡みの実装に手こずりました。)

次回は、AWS End User Messaging(Amazon Pinpoint)と絡めて、今回作ったWebプッシュ通知をリッチにしていきたいと思います。

1
0
0

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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?