PWA | firebase(FCM)を使ったプッシュ通知の仕組みとサンプルコード
2021/10/04
PWAのプッシュ通知は独自にも実装できますが、通知サーバはFirebase Cloud Messaging(FCM)を使うケースが多いようです。ここではFCMを使った通知の仕組みと実装方法について書いていきます。
FCMとは
FCMはGoogleが買収したクラウドサービス「Firebase」にあるFirebase Cloud Messaginになります。アプリやPWAにプッシュ通知を行うためのサービスで、無料で使うことができます。
FirebaseはGoogleアカウントがあれば無料で簡単に始めることができます。
FCMを使ったプッシュ通知の概要
FCMを使ったプッシュ通知は以下のような仕組みになります。
- FCMでアプリ登録とウェブプッシュ証明書を生成する
- SDKを使って、Webサイト(javascript)でトークンを取得する
- (必要に応じ)トークンをサーバに登録する(独自実装が必要)
- プッシュ通知を受信するためのサービスワーカを登録する
- FCMのAPIから通知を行う(トークンを指定して通知)
- サービスワーカがプッシュ通知を受け取る、通知を表示する
Firebaseのプロジェクトを作成しFCMのアプリ登録を行う
以下のサイトからFirrebaseのプロジェクトを作成します。(Googleアカウントが必要)
https://console.firebase.google.com
画面左のメニューから「Cloud Messaging」を選択し、アプリ(Web)を追加します。
Web PUSH証明書を生成する
左側のメニュー上にある設定ボタンをおし、「プロジェクトの設定」を開きます。
「プロジェクトの設定」のタブの「Cloud Messaging」を開き、画面下の方にあるウェブプッシュ証明書の「Generate key pair」を押して、証明書を払い出します。
トークン取得処理の実装
お試しなのでプレーンなjavascriptでの実装になりますが、以下のscriptタグをWebサイトのbodyタグ直下に配置します。
getTokenメソッドでFCMサーバからトークンを取得してサービスワーカを登録します。 取得したトークンはプッシュ通知の送り先になるので、開発時はトークンをサーバに送信してDBに登録しておくなどが必要になりますが、ここではその処理は割愛しています。
代わりにトークンをコンソールログに出していて、後でプッシュ通知を送るときに利用します。
<script type="module">
//FCMのSDKをインポート
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-app.js";
import { getAnalytics } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-analytics.js";
import { getMessaging, getToken } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-messaging.js";
const firebaseConfig = { //ここはfirebaseプロジェクトアプリ登録時に払い出されたコードをコピペ
apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "pwa-test-xxxxx.firebaseapp.com",
projectId: "pwa-test-xxxxx",
storageBucket: "pwa-test-xxxxx.appspot.com",
messagingSenderId: "xxxxxxxxx",
appId: "1:xxxxxxxxxxxx:web:xxxxxxxxxxxxxxxxxxxx",
measurementId: "xxxxxxxxxx"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
// FCMテスト(画面のボタン押下時に実行)
function push_test() {
const messaging = getMessaging();
navigator.serviceWorker.register('./firebase-messaging-sw.js').then(registration => {
getToken(messaging, {
vapidKey: 'xxx', // ウェブプッシュ証明書のキーを貼り付ける
serviceWorkerRegistration: registration //サービスワーカを指定(省略するとルートディレクトリのfirebase-messaging-sw.jsを登録しようとする)
}).then((currentToken) => {
console.debug('token is ' + currentToken);
if (currentToken) {
// 省略:トークンをサーバに送ってDBに登録する(そうしないとトークンはサーバ側で分からない)
} else {
console.log('No registration token available. Request permission to generate one.');
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
});
}).catch(err => {
console.log('ServiceWorker registration failed.');
});
}
document.getElementById('pwa-test').onclick = push_test;
</script>
サービスワーカーの実装
プッシュ通知を受け取るための処理をサービスワーカの「firebase-messaging-sw.js」ファイルを作って実装します。
importScripts('https://www.gstatic.com/firebasejs/9.1.0/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/9.1.0/firebase-messaging-compat.js');
firebase.initializeApp({
apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "pwa-test-xxxxx.firebaseapp.com",
projectId: "pwa-test-xxxxx",
storageBucket: "pwa-test-xxxxx.appspot.com",
messagingSenderId: "xxxxxxxxx",
appId: "1:xxxxxxxxxxxx:web:xxxxxxxxxxxxxxxxxxxx",
measurementId: "xxxxxxxxxx"
});
const messaging = firebase.messaging();
// 起動していない時の通知処理
messaging.onBackgroundMessage(function (payload) {
console.log('[BG] Received background message ', payload);
const notificationTitle = payload.data.title;
const notificationOptions = {
body: payload.data.message,
tag: payload.data.tag,
icon: './logo.png',
badge: './badge.png',
image: './image.png',
vibrate: [300, 100, 100, 100, 300],
actions: [{
action: 'action1',
title: 'アクション1'
},
{
action: 'action2',
title: 'アクション2'
}
]
};
self.registration.showNotification(notificationTitle,
notificationOptions);
});
「onBackgroundMessage」はWebアプリがバックグラウンドにいる時にプッシュ通知を受けると実行されます。payloadにはPUSH通知の中身が入っているため、これをnotification情報に移し、通知を表示する処理を記述しています。
notificationで指定できる情報の詳細は別の記事に書きますが、ここではよく使いそうなtitleやbody、iconなどを指定しています。
まとめ
firebaseを使ったトークンの取得とサービスワーカーでの通知受信処理まで紹介しました。あとはfirebaseからPUSH通知を行えば通知が表示されますが、それは次の記事で書いていきたいと思います。