PWAのプッシュ通知の仕組み

エンジニアの山本です。

Progressive Web Applicationの入門記事『PWA対応をするためにやった最低限のこと』に続きまして、プッシュ通知を実装していきたいのですが、その前にWebプッシュの仕組みを理解する必要があると感じたため、本投稿ではどのようにWebプッシュが機能しているのかについて、説明します。

Webプッシュの登録から送信まで

Webプッシュに関して、大きく分けて2つの段階があります。

  1. 登録が完了するまでの準備段階
  2. プッシュ通知を送信する実行段階

この仕組みを段階的に確認しながら、プッシュ通知の理解を深めていただければと思います。

(準備段階)Webプッシュの登録が完了するまでのフロー

Webプッシュの登録が完了するまでのフロー まずは、Webプッシュの登録が完了するまでのフローをみていきます。 全体像はこんな感じです。

ステップ1 ユーザーに任意のタイミングで許可ダイアログを表示

Webプッシュの登録が完了するまでのフロー ステップ1 まず最初にユーザーがアクセスしてきたら、任意のタイミングでプッシュ通知送信許可のためのダイアログを開く ServiceWorker.pushManager.subscribe() メソッドを実行します。

考察:プッシュ通知の許可ダイアログはいつ表示すべきか

少し本題とは逸れるのですが、ダイアログの表示タイミングについて少し考えていたことを書いておきます。

前提として、許可するメソッドは、読み込み時に実行させることもできますし、onclickなどのユーザーアクションをトリガーにすることもできます。

私は、プッシュ通知の許可は、ユーザーの体験を向上させるような設計にしておく方が良いと個人的には思います。

サイトに訪問してすぐ、『プッシュ通知を受け取りますか?』とダイアログを表示させると、 ユーザーがまずサイトのことを理解していないため『いいえ』となる確率が高そうです。

またこの場合、副次的な体験の低下もあるかもしれません。 プッシュ通知を許可しないユーザーが増えているので、 通知を受け取るかどうかを聞かれること自体があまり好ましいものではないと感じる可能性があります。 サイトに来た時点では少なくとも検索ツールからの流入である場合は、 サイトのコンテンツに興味があって来ているので、ユーザーの目的であるサイトのコンテンツを閲覧するということの障害となり得ます。

なので、サイトにアクセスして、ユーザーがサイトを理解し、 さらに、通知の内容が有益な情報であるということを訴求した上で、ダイアログを表示するのが理想ではないでしょうか。

ステップ2 メッセージングサービスへの通信

Webプッシュの登録が完了するまでのフロー ステップ2 話を戻しますが、プッシュ通知の送信がユーザーに許可されたら、 プッシュ通知が許可されたということをメッセージングサービスへ登録させる必要があります。 実は、上述のメソッドでメッセージングサービスへの送信までやってくれます。

ServiceWorker.pushManager.subscribe() が行うことはこれら2つです。

  1. ユーザーにプッシュ通知の許可ダイアログを表示する
  2. 許可されたらメッセージングサービスへ送信する

実際のコードはこのように、2つの引数を取ります。これらは必須です。

ServiceWorker.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: applicationServerKey
})

userVisibleOnly

  • こちらは、プッシュが送信されるたび通知を表示するという項目なので、基本的には true となります。

applicationServerKey

  • 今回は、Vapidキーの前提で話を進めますが、こちらの項目では公開鍵を渡します。

鍵の生成は様々な手段で作成することができると思いますが、個人的にはweb-push-libs/web-push: Web Push library for Node.jsなどを活用するのが良いと思います。 こちらのレポジトリをnpmでグローバルにインストールして、下記のコマンドを実行すれば鍵が生成されるのでそれを使うことができます。

Usage:
  web-push generate-vapid-keys [--json]

ステップ3 メッセージングサービスからのレスポンス

Webプッシュの登録が完了するまでのフロー ステップ3 送信した情報の登録が完了すると、そのレスポンスとしてPush Subscriptionオブジェクトが返ってきます。 (説明のためPromiseについては割愛しております)

このPush Subscriptionオブジェクトは、このような構造になっております。

{
  "endpoint": "https://random-push-service.com/some-kind-of-unique-id-1234/v2/",
  "keys": {
    "p256dh" :
"BNcRdreALRFXTkOOUHK1EtK2wtaz5Ry4YfYCA_0QTpQtUbVlUls0VJXg7A8u-Ts1XbjhazAkj7I99e8QcYP7DkM=",
    "auth"   : "tBHItJI5svbpez7KI4CCXg=="
  }
}

ステップ4 データベースに保存

Webプッシュの登録が完了するまでのフロー ステップ4 上述のPush Subscription オブジェクトは、プッシュ通知の送信者と受信者をつなぐものでプッシュ通知を送信する際に必要な情報です。 そのため、一度プッシュ通知を許可したユーザーに対して、何度もプッシュ通知を送りたければこれを保存しておく必要があります。

subscribe() メソッドのレスポンスとして Push Subscription オブジェクトを受け取ったら、 非同期通信などで自前のアプリケーションに送信して、データベースに保存します。 データベースに保存するときは、JSONオブジェクトをそのまま使用できるPostgreSQLを使用するのも良いと思いますし、MySQLなどであれば JSON.stringify() メソッドなどで、文字列に変換して保存しておいても良いと思います。

おそらく、一般的なチュートリアルで最もわかりにくいのがこのプロセスなのではないでしょうか。 Service Workerやサイト上のJavascriptを作成する方法を説明している記事が多いものの、このプロセスが曖昧なことが多いと感じました。

私はこの自前のアプリケーションをNode.jsで作成しましたが、ご自身が得意な環境で作成するのが良いと思います。

(実行段階)Webプッシュ通知を送信するときのフロー

Webプッシュ通知を送信するときのフロー 全体像 次に、保存したPush Subscription オブジェクトを使用して、プッシュ通知を送信するまでのフローをみていきます。 ここからは、上述の通り自前のアプリケーションでの作業になりますので、環境ごとに記述方法が異なります。 今回は先ほどVapidキーの生成時に使用したこちらのライブラリ: web-push-libs/web-push のメソッド名を例として説明します。

ステップ1 メッセージングサービスへ送信したいデータを送信する

Webプッシュ通知を送信するときのフロー ステップ1 まずは、データベースに保存してあるユーザーのPush Subscription オブジェクトを作成し、文字列であれば JSON.parse() メソッドなどで一度JSON形式に戻しておきます。

web-push ライブラリでは、先ほど生成したVapidキーをセットし sendNotification() メソッドを実行することでメッセージングサーバーに通信できます。

require('web-push').setVapidDetails(
  'mailto:mail@example.com',
  vapidPublicKey,
  vapidPrivateKey
).sendNotification(pushSubscription, notificationContent)

2つの引数を渡すことができます。

  1. Push Subscription オブジェクト
  2. プッシュ通知のコンテンツ

Push Subscription オブジェクトは、上でデータベースから取得したユーザーのPush Subscription オブジェクトです。

プッシュ通知のコンテンツは下記の様な内容になります。

notificatinoContent = {
    body: 'メッセージbody',
    icon: 'images/icon.png',
    badge: 'images/badge.png'
};

ステップ2 Pushデータがデバイスに転送される

Webプッシュ通知を送信するときのフロー ステップ2 sendNotification() メソッドが実行され、メッセージングサーバーへ諸情報が送信されます。 全ての情報が正常に認識されると、メッセージングサービスからそれぞれのデバイスにデータが送られます。

ステップ3 Service Workerにデータが渡され、その中の指定通りに処理される

Webプッシュ通知を送信するときのフロー ステップ3 送信されてきたデータがService Workerに渡されると、pushイベントが発火します。 プッシュ通知はデータが送信されてきただけで表示されるものではなく、Service Workerの push イベントをトリガーとして showNotification() メソッドを実行することで初めてプッシュが表示されます。

this.addEventListener('push', function(event) {
    var notificationDataObj = event.data.json();
    var content = {
        body: notificationDataObj.body,
        icon: notificationDataObj.icon,
        badge: notificationDataObj.badge
    };
    event.waitUntil(self.registration.showNotification('タイトル', content));
});

イベント発火時にデータを受け取り、そのデータの中からコンテンツを取り出し、それを showNotification() メソッドに引数として渡します。

所感

説明は以上となります。大まかな流れは把握できましたでしょうか。 この技術により、ユーザーとのコミュニケーションがより密接になります。今までは、アプリにしなければプッシュ通知が送信できませんでしたが、これからはブラウザサイトでも送信することができるようになりました。 現段階では、技術がリリースされて、それに興味を持ったサイトが次々導入している状態だと思います。 ただ、この記事の初めの方でも少し述べましたが、ユーザーとのコミュニケーションという視点でベストプラクティスを確立できているサイトは少ないように思います。

ユーザーの生活の一部としてプッシュ通知を活用することを考えると、よりユーザーの体験というものを意識する必要があるのではないでしょうか。

もしそういったことでお困りの方がいらっしゃれば、最下部のメールアイコンからお問い合わせください。

この記事を書いた人 yamamoto 営業を経験した後、現在はフロントエンドとバックエンドをメインで担当。
エンジニアっぽくないエンジニアと言われることが多い。
好きな漫画 NARUTO/ワンピース/バガボンド
TOP