【PWA入門】WebPushを実装する。

先日投稿した記事でPWAについて触れました、この記事ではPWAに使用されていたServiceWorkerでできることの一つであるWebPushを試してみたいと思います。まずはServiceWorkerの概要です。
ServiceWorkerとは
- JavaScript Worker のひとつ
- プログラム可能なネットワークプロキシ、ページからのリクエストをコントロールできる。
- DOMに直接アクセスできない、アクセスする場合はページ経由でできる。
- 登録されたjs内ではステートは保管できないので、IndexedDB、CacheStorageなどを利用する必要がある。
- localhostまたはhttpsのページでのみ動作する。
何かしらのイベントに紐付いて動いて、処理が完了すると状態が破棄されるプロセスのようなイメージです。
働きとしては、複数のウェブアプリケーション間やブラウザ、ネットワークとの間でプロキシサーバのようなもので、具体的にできることとしては以下の2つがよく挙げられてます。
- アプリのオフライン対応
- プッシュ通知
Web Workers APIの一種として動いているようで、windowとは別のグローバルのコンテキストでコードが実行されます。
ServiceWorkerの利用
// app.js index.htmlで読み込む際にjsファイルをServiceWorkerに登録する
navigator.serviceWorker.register('sw.js');
// sw.js イベントに対して何かしらの処理を紐付ける
self.addEventListener('fetch', async function (event) {
console.log(event);
});
こんな感じで、fetchなどネットワークリクエストをインターセプトして処理を加えたりできます。
プッシュ通知を実装してみる
前回のPWAを作った記事でオフライン対応を行ったので、この記事では前回の記事と同じリポジトリでプッシュ通知の追加 | Web | Google Developersを参考にプッシュ通知を実装してみました。作成したアプリのコードを抜粋してみていきます。
ServiceWorkerへsw.jsを登録
window.addEventListener('load', async function (e) {
// ServiceWorkerに対応しているブラウザか判定
if('serviceWorker' in navigator) {
try {
// sw.jsをサービスワーカーに登録
swRegistration = await navigator.serviceWorker.register('sw.js');
console.log(`SW registered`);
} catch (error) {
console.log(`SW not registered`);
}
// PushManagerに対応しているか確認する
if ('PushManager' in window) {
console.log('Push is supported');
initializeSubscribeBtn();
} else {
console.warn('Push messaging is not supported');
subscribeButton.textContent = 'Push Not Supported';
subscribeButton.disabled = true;
}
}
});
WebPushにはServiceWorker
とPushManager
が必要になるため対応しているか確認しています。2018/10/31時点ではIEやSafariがPushManager
に対応していないようです。
sw.jsでpushイベントをリッスンしてNotificationを表示する。
self.addEventListener('push', function (event) {
console.log('[Service Worker] Push Received.');
console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);
const title = 'Push Codelab';
const options = {
body: 'Yay it works.',
icon: './images/icon.png',
badge: './images/badge.png'
};
// waitUntilはPromiseを受け取り、そのPromiseが解決されるまで処理を継続する。
event.waitUntil(self.registration.showNotification(title, options));
});
これでpush
イベントが発行された際に処理が走ります。とりあえずpushイベントのテスト用にすべてのデータをべた書きしてます。
試しにpush
イベントを発行するには、ChromeのdevtoolのApplicationタブからPush
をクリックします。
すると通知が表示されました。
※Chromeのバージョンが69.0.3497.100
だとdevtoolからpushイベントが発行されないので注意です。edge版のChrome Canaryだと発行されました。
Notificationクリック時の動作を追加
sw.js内で、notificationclick
イベントへ処理を紐付けます。
self.addEventListener('notificationclick', function (event) {
console.log('[Service Worker] Notification click Received.');
...
});
app.jsでPermission要求後、PushManagerへSubscriptionを登録
ここからはPushイベントをサーバーから発行するための処理です、PushServerにはPush Companionを使用しています。
適当にSubscribeボタンを設置して、クリック時に発火するようにします。
<button id="subscribeButton">Subscribe</button>
async function initializeSubscribeBtn() {
subscribeButton.addEventListener('click', function () {
subscribeButton.disabled = true;
if (isSubscribed) {
unSubscribeUser();
} else {
subscribeUser();
}
});
...
}
function subscribeUser() {
// PushCompanionで発行されたPublicKeyをArrayBufferに変換
const applicationServerKey = urlB64ToUint8Array(applicationServerPubKey);
swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: applicationServerKey
})
.then(function (subscription) {
console.log('User is subscribed');
console.log(JSON.stringify(subscription));
...
})
.catch(function (err) {
console.log(`Failed to subscribe the user: ${err}`);
...
});
}
これでWebPushの準備ができました。
実際に動かしてみる。
デモページを開き、Subscribeボタンをクリックし、通知の許可をすると、subscribeUser()
内のconsole.log(JSON.stringify(subscription));
でsubscription
がJSONで表示されるのでそれをコピーしてPush CompanionのSubscription to Send To へ貼り付け、SEND PUSH MESSAGEボタンをクリックすると、Notificationが表示されます!
終わりに
今回作成したアプリは、鍵の発行とサーバーへのサブスクリプションの登録を手動で行っていたため、購読するまでに割と手間がかかってしまいます。実際に使用する際にはそういった箇所をAPIなどで処理することが必要になってきそうです。
参考
プッシュ通知の追加 | Web | Google Developers
Can I use… Support tables for HTML5, CSS3, etc