プッシュ通知はサーバーからアプリに対して通知を送信できるもので、スマートフォンのアプリ等で日常的に触れていると思います。今回はWebアプリでプッシュ通知を利用する為のPushAPIについて調べたことを紹介したいと思います。
PushAPI
Webアプリがプッシュ通知を利用できるようにPushAPIが用意されています。用意されたAPIを利用することで簡単にプッシュ通知が利用できるようになっています。 developer.mozilla.org
Service Worker
PushAPIの説明に入る前にService Workerについて軽く説明します。
developer.mozilla.org Service WorkerはWebアプリとは別スレッドで動作し、Webアプリ、ブラウザー、ネットワークの間にいるプロキシサーバーのような存在です。WebアプリごとにこのService Workerが登録でき、ここでリソースのアクセスの間に入りキャッシュを返したり、今回説明するプッシュ通知を受け取るような仕事を行います。詳しくはリンク先の説明をお読み下さい。
プッシュ通知の流れ
W3Cのページのシーケンス図がわかりやすいのでこれを借ります。 上図の
- web page
- service worker
- user agent
はそれぞれのWebアプリのユーザー側にあるものでuser agentはブラウザにあたります。そして
- push service
- application server
はそれぞれサーバーです。
シーケンス図でメッセージを送るまでの流れは以下となりますね。
- まずService Workerの登録を行う(register())
- プッシュ通知の購読処理を行う(subscribe())
- push serviceに購読処理が行われpush先のendpoint等が返ってくるので、それをapplication serverに渡す
- application serverはpush serviceに、push serviceはブラウザに、ブラウザはservice workerにとpush messageが伝搬する
- service workerでpush eventに対する処理を登録しておき、そこから例えば画面への通知などを実行する
push service
push serviceとは各ブラウザのベンダーが用意しているサーバーでこれがブラウザとapplication serverとを仲介しています。ブラウザによって利用しているものは異なります。
- Chrome や Sleipnir for Windows
- Firebase Cloud Messaging
- Firefox
- Mozilla Push Service
- Edge
- Windows Notification Service
動かしてみる
以下のサイトを一通り読むと使い方がわかります。
ここではどんな雰囲気のコードが必要かをかいつまんで見ていきたいと思います。 まずはserviceWorkerを用意します。 本来install, activate, fetchなど色々なeventについて記述しますが今回は説明に必要なpush eventについてのみ書いています。 これを書くことでプッシュ通知が着た際に中のcallbackが動いてくれます。下のコードではログを表示した後showNotifidationでデスクトップ通知を出しています。
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 Recieved!!!"; const options = { body: event.data.text() }; event.waitUntil(self.registration.showNotification(title, options)); });
次にシーケンス図にあったようにservice workerを登録しなければなりません。 以下のようなコードをページを読み込む際に実行されるようにしてservice workerを登録します。 やっていることの流れは以下です。
- まずservice workerの登録をする
- 返ってきたregistrationのpushManagerからプッシュ通知の購読情報を取得
- もし購読していない場合には同じくpushManagerのsubscribeで購読処理を行う
if ("serviceWorker" in navigator) { window.addEventListener("load", async () => { // service workerの登録 const registration = await navigator.serviceWorker .register("/sw.js") .catch(console.error); console.log("sw registered."); // push通知のsubscriptrionの入手 let subscription = await registration.pushManager.getSubscription(); console.log(JSON.stringify(subscription)); // subscribeされていない場合にsubscribeを実行 if (!subscription) { subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: "<key>" }); console.log(`subscribed! endpoint: ${subscription.endpoint}`); } }); }
subscribeでは引数としてapplicationServerKeyというものを渡しています。push serviceがapplication serverを認証するのに使用する公開鍵でapplication serverが対応し、事前に生成しておく必要があります。またchromeではこれが必須となっているようです。
準備OK
Webアプリ側は上記の手順が出来れば準備はできました。application serverがpush serviceにメッセージを送信することでservice workerのpush eventまで伝搬してくれます。簡単!!
chromeのdeveloper toolではプッシュ通知のテストが出来ます。画像のPushボタンを押すことでservice workerのpush eventが発火してくれます。
どういう状態なら受け取れるのか?
プッシュ通知が受け取れることはわかりました。この通知はWebアプリが開いているときのみ受け取れるのでしょうか?実際に試してみました。 上で紹介したcode labの中でプッシュ通知送信を行えるWebアプリがリンクされていたのでそれを用いました。こちらで生成した鍵でsubscribeし、endpointをコピペしてアプリからプッシュ通知を送信します。 web-push-codelab.glitch.me
結果的に一度購読すればブラウザが起動している間はプッシュ通知を受け付けてくれます。当該Webアプリが起動していなくてもプッシュ通知が受け付けてくれますし、Service Worker自体がStopしていてもpush eventによってService Workerが起動してプッシュ通知を受け付けます。またブラウザを閉じ、再度起動した際にもService Workerの登録やプッシュ通知のsubscribe自体は有効なようでプッシュ通知を受け付けてくれました。
まとめ
Push APIを使ってプッシュ通知を実装するのは見てきたとおり結構簡単に出来、しかもWebアプリを起動していなくてもブラウザが動いていれば通知が受けられる強力なものでした。送信するapplication server側については触れていませんのでそちらで何か大変なことがあるかもしれませんが、fcmなどのサービスを利用すればそちらもケースによっては結構簡単に扱えるのではないかと思います。 Progressive Web Appという概念がありますがプッシュ通知ができることは大事な部分の一つだと思うのでぜひ理解しておきたかったもので今回勉強出来て良かったです。