Don't miss out on the action at this year's Chrome Dev Summit, happening on Oct 23rd and 24th. Learn more.

パーミッションのリクエストとユーザーのサブスクライブ

通知に対するパーミッションのリクエストやユーザーのサブスクライブは、通知を表示したときに 1 タップで手軽に実行できるようにする必要があります。

このセクション以降では、実際のコードを紹介します。これらの短いコードを実装する場所を明確に理解しておくことが重要です。ここでは、Service Worker についての理解が重要になります。パーミッションのリクエストとユーザーのサブスクライブのコードは、Service Worker のコードではなく、アプリのコードで実行されます。Service Worker は後ほど、プッシュ メッセージを処理してユーザーに表示する際に使用します。

パーミッションの確認

ページを読み込むときは必ず、既存のパーミッションを確認してください。パーミッションがすでに付与されている場合、すぐに通知の送信を開始できます。いずれの場合も、この情報をもとにパーミッションの状態を設定します。以下に例を示します。わかりやすくするため、まだ何もリクエストしません。

注: 簡潔にするため、この例では、本来実行すべき多くの機能チェックを省略しています。 完全な元のコードは、Google の GitHub のサンプル リポジトリを参照してください。

function initialiseState() {
  if (Notification.permission !== 'granted') {
    console.log('The user has not granted the notification permission.');
    return;
  } else if (Notification.permission === “blocked”) {
   /* the user has previously denied push. Can't reprompt. */
  } else {
    /* show a prompt to the user */
  }

  // Use serviceWorker.ready so this is only invoked
  // when the service worker is available.
  navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    serviceWorkerRegistration.pushManager.getSubscription()
      .then(function(subscription) {
        if (!subscription) {
          // Set appropriate app states.
          return;
        }
      })
      .catch(function(err) {
        console.log('Error during getSubscription()', err);
      });
  });
}

ページの読み込み時のサブスクリプション リクエストを回避する

前の例では、pushManager.subscribe() を呼び出していない点に注意してください。これは、既存のサブスクリプションが見つからない場合には当然のレスポンスのように思えます。このようなリクエストはタイムリーに見えるかもしれませんが、ユーザーについてまだ何も把握しておらず、ユーザー側もこちらの情報を知らない可能性があるため、的確で有用なメッセージを送信するのは困難です。

パーミッションのリクエスト

通知の送信前に許可を求め、その目的を説明する

タイミングによらず、パーミッションをリクエストする処理は 2 つのプロセスから成ります。まずは、通知の送信目的を明確に説明したメッセージを使用して、アプリから通知を送信する許可を求めます。

ユーザーが同意したら、Push Manager からサブスクリプションを取得できます。取得するには、PushManager.subscribe() を呼び出します(以下の例で強調表示されている箇所)。この例では、userVisibleOnlytrue に設定したオブジェクトを渡して、通知を常にユーザーに表示するようにブラウザに指定します。また、applicationServerKey も渡します。

if ('showNotification' in ServiceWorkerRegistration.prototype) {
  navigator.serviceworker.ready
  .then(registration => {
    return registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: new Uint8Array([...])
    });
  })
  .then(subscription => {
    // Do something with the subscription.
  })
  .catch(error => {
    // Do something with the error.
  });
}

これは Chrome で実行した結果です。

Chrome のパーミッション要求画面

applicationServerKey とは

applicationServerKey の値は、サーバーで生成する必要があります。サーバー側のすべての問題については、次のセクションで説明します。 ここではまず、applicationServerKey について把握する必要があります。 subscribe() 呼び出しでキーを渡すときは、Uint8Array(8 ビットの符号なし整数の配列)であることを確認してください。

特定のアクションをトリガーにする

特定のアクションによるプロンプト

特定の状況で実行されたアクションのレスポンスとして、通知を送信するパーミッションを求めます。 この方法では、通知をユーザーの目標と関連付けて、通知を送信する目的を明確に示すことができます。

たとえば、航空会社のサイトでフライトの遅延をユーザーに通知したい場合、目立つ場所にオプトイン チェックボックスを表示して、ユーザーがオプトインを選択した場合にのみ通知のパーミッションを求めます。

通知を管理する場所を提供する

ユーザーが容易にサイトの通知を変更したり、無効にできるようにしてください。そうすれば、ユーザーがブラウザや端末レベルで通知を無効にするのを防ぐことができます。

目立つ場所に、通知のスイッチを追加します。また、通知の実装方法ではなく、送信する内容がユーザーにわかるように、ラベルを付けます。 デベロッパーがソユーズ宇宙船の軌道調整方法を知らないように、ユーザーは「プッシュ通知」と表示されても何のことか分かりません。

推奨: 以後表示する通知の内容を示す通知スイッチ。
非推奨: 通知の実装方法しか伝わらない通知スイッチ。

サーバーにサブスクリプションを渡す

通知を送信するパーミッションを取得し、関連するコントロールの状態を設定したら、サブスクリプション情報(仕様では「プッシュ リソース」と呼ばれる)をプッシュ サーバーに送信する必要があります。それには、サブスクリプション データを含む適切な request オブジェクトを作成してサーバーに渡します。

リクエストを作成する際は(以下の例で強調表示)、POST 動詞と application/jsonContent-Type ヘッダーを使用します。 本文用に、subscription オブジェクトを文字列に変換する必要があります。 次のセクション、メッセージの送信で、このオブジェクトの中身を見ていきます。 fetch() を使用して、サーバーにサブスクリプション リクエストを送信します。

if ('showNotification' in ServiceWorkerRegistration.prototype) {
  navigator.serviceworker.ready
  .then(registration => {
    return registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: new Uint8Array([...])
    });
  })
  .then(subscription => {
    var fetchOptions = {
      method: 'post',
      headers: new Headers({
        'Content-Type': 'application/json'
      }),
      body: JSON.stringify(subscription)
    };
    return fetch('/your-web-server/api', fetchOptions);
  })
  .catch(error => {
    // Do something with the error.
  });
}