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

Blog Single

先日投稿した記事で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にはServiceWorkerPushManagerが必要になるため対応しているか確認しています。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 CompanionSubscription to Send To へ貼り付け、SEND PUSH MESSAGEボタンをクリックすると、Notificationが表示されます!

終わりに

今回作成したアプリは、鍵の発行とサーバーへのサブスクリプションの登録を手動で行っていたため、購読するまでに割と手間がかかってしまいます。実際に使用する際にはそういった箇所をAPIなどで処理することが必要になってきそうです。

参考

プッシュ通知の追加  |  Web  |  Google Developers

JavaScript Worker

Can I use… Support tables for HTML5, CSS3, etc

Push notification isn’t working when I launched a push from application tab in chrome. · Issue #49 · GoogleChromeLabs/web-push-codelab

 

Posted by MachidaMamoru
typescriptとAngularが好き、最近Dapps開発にハマっています。

Other Posts: