PWAの基礎知識(その9)「Push API/VAPID編」
Webブラウザにプッシュ通知を送れるPush APIは魅力的な機能ですが、各ブラウザによって実装が異なっていたり、Firebaseでプロジェクトを登録したりするのが手間でした。それを共通化し、さらにプロジェクト登録不要で使えるようにする仕組みがVAPIDになります。
今回はこのVAPIDを使ったPush APIの使い方です。
必要なもの
今回はVAPIDに対応したライブラリ、web-pushを使います。今回はNode.jsのライブラリです。zaru/webpushというRuby向けのライブラリもあります。
初期設定
まずサーバ側で秘密鍵と公開鍵を用意します。これは以下のコードで可能です。
const webpush = require("web-push"); | |
JSON.stringify(webpush.generateVAPIDKeys()); | |
// '{"publicKey":"BDC..tP4","privateKey":"Kne…Km8"}' |
この内容を application-server-keys.json
などとしてサーバに保存しておきます。
JavaScriptの準備
Service Worker
VAPIDを使った場合の魅力として、ペイロードが送れるという点が挙げられます。メッセージ付きで送れるので、クライアントサイドで処理判別ができます。そのための処理をService Worker用のJavaScriptに入れておきます。
self.addEventListener('install', () => { | |
console.log('ServiceWorker がインストールされました'); | |
}); | |
self.addEventListener('push', ev => { | |
// payloadの取得 | |
const {title, msg, icon} = ev.data.json(); | |
self.registration.showNotification(title, { | |
icon: icon, | |
body: msg | |
}); | |
self.registration.pushManager.getSubscription().then(subscription => { | |
console.log(subscription); | |
}, err => console.log(err)); | |
}); |
クライアントサイド
次にクライアントサイドですが、まずService Workerを読み込みます。そして、その後プッシュ通知の登録状態を確認しています。
if (!('serviceWorker' in navigator)) { | |
// Service Worker非対応 | |
} | |
navigator.serviceWorker.register('./serviceworker.js') | |
.then(() => { | |
if (!('showNotification' in ServiceWorkerRegistration.prototype)) { | |
// プッシュ通知がサポートされていない場合 | |
return; | |
} | |
if (Notification.permission === 'denied') { | |
// プッシュ通知を拒否された場合 | |
return; | |
} | |
if (!('PushManager' in window)) { | |
// PushManagerが存在しない場合 | |
return; | |
} | |
return navigator.serviceWorker.ready; | |
}) | |
.then(serviceWorkerRegistration => { | |
return serviceWorkerRegistration.pushManager.getSubscription(); | |
}) | |
.then(subscription => { | |
if (!subscription) { | |
// すでに購読中 | |
} else { | |
// 未購読 | |
console.log(subscription.toJSON()); | |
} | |
}) |
購読を開始する際のイベントは次の通りです。大事なポイントとして、 applicationServerKey
を追加しています。ここには公開鍵をunit8にしたものを適用します(後述)。
navigator.serviceWorker.ready | |
.then(serviceWorkerRegistration => { | |
return serviceWorkerRegistration.pushManager.subscribe({ | |
userVisibleOnly: true, | |
applicationServerKey: convertedVapidKey | |
}); | |
}) | |
.then(subscription => { | |
// 購読開始 | |
console.log(subscription.toJSON()); | |
}) |
公開鍵の設定です。 vapidPublicKey
には web-push で生成した公開鍵を指定します。
const vapidPublicKey = 'BOO…YEk'; | |
const convertedVapidKey = urlBase64ToUint8Array(vapidPublicKey); | |
function urlBase64ToUint8Array(base64String) { | |
const padding = '='.repeat((4 – base64String.length % 4) % 4); | |
const base64 = (base64String + padding) | |
.replace(/\-/g, '+') | |
.replace(/_/g, '/'); | |
const rawData = window.atob(base64); | |
const outputArray = new Uint8Array(rawData.length); | |
for (let i = 0; i < rawData.length; ++i) { | |
outputArray[i] = rawData.charCodeAt(i); | |
} | |
return outputArray; | |
} |
Web Pushの購読データ(subscription)をJSON出力すると、次のような内容になっています。このデータをすべて保存しておきます(サーバなど)。
{ | |
"endpoint":"https://fcm.googleapis.com/fcm/send/e7K…Hag", | |
"expirationTime":null, | |
"keys":{ | |
"p256dh":"BK7…e5U", | |
"auth":"DGt…D8g" | |
} | |
} |
Web Pushを送る
ではサーバからプッシュ通知を送ってみましょう。コードは以下のようになります。まず鍵の設定と受信者を指定します。
const webpush = require("web-push"); | |
const keys = require("./application-server-keys.json"); | |
webpush.setVapidDetails( | |
"mailto:admin@moongift.jp", | |
keys.publicKey, | |
keys.privateKey | |
); | |
const subscribers = [ | |
{ | |
"endpoint":"https://fcm.googleapis.com/fcm/send/e7K…Hag", | |
"expirationTime":null, | |
"keys":{ | |
"p256dh":"BK7…e5U", | |
"auth":"DGt…D8g" | |
} | |
} | |
]; |
そしてプッシュ通知を送ります。 icon で表示するアイコンを指定しています。
const icon = `app.png`; | |
const params = { | |
title: "プッシュ通知です!", | |
msg: `これはサーバから送っています. 今は ${new Date().toLocaleString()} です。 メッセージとアイコンも送っています `, | |
icon: icon | |
}; | |
Promise.all(subscribers.map(subscription => { | |
return webpush.sendNotification(subscription, JSON.stringify(params), {}); | |
})) | |
.then(res => console.log(res)) | |
.catch(err => console.log('ERROR', err)); |
そしてGoogle ChromeやFirefoxに対して通知が送れます。
Firefoxの場合です。
VAPIDを使った場合の利点としては、Firebaseでプロジェクトを作る手間がなく、Webブラウザの共通仕様の基で開発できるということです。なお、Safariはこの仕様に則っていない(そもそも独自仕様)ので、対応できません。
コメントは受け付けていません。