前回は、Navigator.setAppBadgeを使ってアイコンにバッジをつけました 。
今回はトークンをデータベースに登録して、サーバからプッシュ通知を送信してみます。
Safariでプッシュ通知を受け取る条件
❶ httpsでWebサイトをつくる
❷ ❶のウェブサイトをホーム画面に追加する(displayをstandaloneにする)
❸ ユーザーアクションきっかけで通知の許可をとる
が、必要です。
事前準備
❶ Firebaseのプロジェクトを準備する
- Hosting、Messaging、Functions、Cloud Firestoreを有効にします
❷ Firebaseのプランを切り替える
- Sparkプラン(無料)だと、Functionsが利用できないため、Blazeプラン(従量課金制)に切り替えます
❸ ウェブアプリの追加
- プロジェクトの設定 > 全般 > マイアプリ > アプリの追加 からWebAppを追加します
❹ Firebase Cloud Messaging API(V1)を有効にして、ウェブプッシュ証明書を発行する
- プロジェクトの設定 > Cloud Messaging > ウェブの構成 > ウェブプッシュ証明書 の鍵ペアをメモしておきます
ソースコード
public/index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>pwa notification</title> <link rel="manifest" href="/manifest.json" /><!--manifest.jsonを読み込む--> </head> <body> <button class="push">push</button> <textarea id="token"></textarea> <script type="module"> import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.19.0/firebase-app.js'; import { getMessaging, getToken, onMessage } from 'https://www.gstatic.com/firebasejs/9.19.0/firebase-messaging.js'; import { doc, getFirestore, setDoc } from 'https://www.gstatic.com/firebasejs/9.19.0/firebase-firestore.js'; // ❸で発行したfirebaseConfigを記述する const firebaseConfig = { apiKey: 'HOGEHOGE', authDomain: 'FUGAFUGA', projectId: 'HOGEHOGE', storageBucket: 'FUGAFUGA', messagingSenderId: 'HOGEHOGE', appId: 'FUGAFUGA', measurementId: 'PIYOPIYO' }; const app = initializeApp(firebaseConfig); const messaging = getMessaging(app); document.querySelector('.push').addEventListener('click', requestPermission); function requestPermission() { if (!window.Notification) { return; } Notification.requestPermission().then((permission) => { if (permission === 'granted') { const vapidKey = 'HOGEHOGE'; // ❹でメモしたウェブプッシュ証明書の鍵ペアを記述する getToken(messaging, { vapidKey }).then(async (currentToken) => { if (currentToken) { await sendTokenToServer(currentToken); document.getElementById('token').value = currentToken; } }).catch((err) => { console.error(err); }); } }); } async function sendTokenToServer(currentToken) { const db = getFirestore(app); const ref = doc(db, 'pwa-token', currentToken); await setDoc(ref, { token: currentToken // tokenを保存する }); } </script> </body> </html>
public/manifest.json
{ "$schema": "https://json.schemastore.org/web-manifest-combined.json", "name": "push-notification", "short_name": "push-notification", "start_url": ".", "display": "standalone", "background_color": "#000", "description": "プッシュ通知検証", "icons": [{ "src": "./icon.png", "sizes": "192x192", "type": "image/png" }] }
public/firebase-messaging-sw.js
importScripts('/__/firebase/9.19.0/firebase-app-compat.js'); importScripts('/__/firebase/9.19.0/firebase-messaging-compat.js'); importScripts('/__/firebase/init.js'); const messaging = firebase.messaging(); messaging.onBackgroundMessage((evt) => { console.log(evt); });
functions/index.js
const admin = require('firebase-admin'); const functions = require('firebase-functions'); admin.apps.length ? admin.app() : admin.initializeApp({ credential: admin.credential.applicationDefault() }); exports.sendPushNotificationsUseAdmin = functions.https.onRequest(async (_, resp) => { const tokens = await getTokens(); const maxLength = 500; const messagesList = [[]]; tokens.forEach((token) => { messagesList[messagesList.length - 1].push({ token, notification: { title: 'Push通知テスト', body: 'Push通知テスト' } }); if (messagesList[messagesList.length - 1].length === maxLength) { messagesList.push([]); } }); messagesList.forEach((messages) => { if (messages.length) { // admin.messaging().sendAll(messages); admin.messaging().sendEach(messages); } }); return resp.send(JSON.stringify(messagesList)); }); async function getTokens() { const db = admin.firestore(); const query = db.collection('pwa-token').where('token', '>', ''); const snapshot = await query.get(); const tokens = snapshot.docs.map((doc) => { return doc.get('token'); }); return tokens; }
これで、HostingとFunctionsをデプロイし、sendPushNotificationsUseAdminを実行すればプッシュ通知が届くはずです。
tokenがひとつしか登録されていないのであれば、 admin.messaging().sendAll ではなく、admin.messaging().send で送信してしまえば良いのですが、今後tokenが複数登録されることを想定し、500件ずつsendAllsendEachで送信しています。(sendAllが廃止されたためsendEachに変更しました 2024.9.18 追記)