はじめに
今更感はありますが、今までWebプッシュ通知を実装した事がなかったので、Firebase Cloud Messagingを使って実装してみました。
Firebase Cloud Messaging(FCM)は、Googleが提供するプッシュ通知サービスです。
プッシュ通知とは?
プッシュ通知は、iOS、Android、Webなどのデバイスからユーザーに対して通知する機能です。プッシュ通知の種類は、大きく2つに分けられます。
種類 | 説明 | 例 |
---|---|---|
A2P (Application to Person) |
アプリケーションからユーザーへのプッシュ通知 | ・ニュースアプリの最新記事通知 ・ECサイトのセール通知 ・天気予報の警報通知 |
P2P (Person to Person) |
ユーザーからユーザーへのプッシュ通知 | ・メッセージアプリの新着通知 ・SNSの投稿通知 ・チャットの返信通知 |
プッシュ通知の仕組み
プッシュ通知の仕組みは、任意のトリガーによりサーバーからクライアントアプリにプッシュ通知送信します。
アクター | 説明 |
---|---|
ユーザー | アプリケーションを利用するエンドユーザー |
クライアントアプリ | ユーザーが使用するアプリケーション (iOS/AndroidアプリやWebアプリ) |
プッシュ通知サービス | プッシュ通知の配信を管理するサービス (Firebase Cloud Messagingなどのサービス) |
バックエンド | アプリケーションのサーバーサイドシステム |
ブラウザがバックグラウンド時の動作について
ブラウザがバックグラウンド時は、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にプロジェクトを作成する
-
Firebaseのサイトにログインします
https://firebase.google.com/?hl=ja -
途中、AIを有効にするかなど聞かれますが、任意の選択にして「プロジェクトを作成」ボタンを押します
Firebaseにアプリを設定する
FCMのキーペアを作成する
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を利用します。
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と接続するために必要な設定を記述します。
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のデフォルトコードをベースに、フォアグラウンドでプッシュ通知を受信した際の処理を記載します。
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からテストメッセージを送ってみる
(※ 今回は、取得したトークンをコンソールログに表示してたので、開発者ツールを開いてトークンを確認しました。)
フォアグラウンドの場合...
バックグラウンドの場合...
これでひとまずWindows11のChromeブラウザで表示することが出来るようになりました。
所感
今回、Firebase Cloud Messagingを使ってWebプッシュ通知を実装してみました。
よくある技術ではありますが、改め実装しようとすると、意外と難しいなというのが率直な感想です。
(ServiceWorkerとFirebaseのSDKの絡みの実装に手こずりました。)
次回は、AWS End User Messaging(Amazon Pinpoint)と絡めて、今回作ったWebプッシュ通知をリッチにしていきたいと思います。