ウェブアプリにプッシュ通知を追加する

1. 概要

プッシュ メッセージングは、ユーザーとの再エンゲージメントを簡単かつ効果的に行う方法です。この Codelab では、ウェブアプリにプッシュ通知を追加する方法について説明します。

学習内容

  • プッシュ メッセージングのユーザー登録と登録解除の方法
  • 受信したプッシュ メッセージの処理方法
  • 通知の表示方法
  • 通知のクリックへの対応方法

必要なもの

  • Chrome 52 以降
  • Chrome 用のウェブサーバー、または任意のウェブサーバー
  • テキスト エディタ
  • HTML、CSS、JavaScript、Chrome DevTools に関する基礎知識
  • サンプルコード(セットアップするを参照)

2. セットアップする

サンプルコードをダウンロードする

この Codelab のサンプルコードを取得する方法は 2 つあります。

  • git リポジトリのクローンを作成します。
git clone https://github.com/GoogleChrome/push-notifications.git
  • zip ファイルをダウンロードします。

ソースを ZIP ファイルとしてダウンロードした場合は、解凍するとルートフォルダ push-notifications-master が作成されます。

ウェブサーバーをインストールして確認する

任意のウェブサーバーを使用できますが、この Codelab は Chrome アプリ用のウェブサーバーで適切に動作するように設計されています。このアプリをまだインストールしていない場合は、Chrome ウェブストアから入手できます。

Chrome アプリ用のウェブサーバーをインストールしたら、ブックマーク バーの [アプリ] ショートカットをクリックします。

946bcaaad66e5c8e.png

[アプリ] ウィンドウで、ウェブサーバーのアイコンをクリックします。

9f3c21b2cf6cbfb5.png

次にこのダイアログが表示され、ローカル ウェブサーバーを構成できます。

73543edeb27c3d6f.png

[フォルダの選択] ボタンをクリックし、ダウンロードした push-notifications フォルダ内の app フォルダを選択します。これにより、ダイアログの [ウェブサーバーの URL] セクションに表示される URL を介して、作業中の作業を行うことができます。

[オプション] で、次のように [index.html を自動的に表示] の横にあるチェックボックスをオンにします。

5ac11bca86ce7369.png

次に、[ウェブサーバー: 開始] トグルを左にスライドしてサーバーを停止し、右に戻して再起動します。

d42f87972f9fec24.png

ウェブサーバーの URL をクリックして、ウェブブラウザでサイトにアクセスします。次のようなページが表示されます。バージョンによっては、アドレスが 127.0.0.1:8887 と表示される場合があります。

00-push-codelab.png

常に Service Worker を更新する

開発中は、Service Worker が常に最新の状態であり、最新の変更が適用されていることを確認すると便利です。

Chrome で設定するには:

  1. [プッシュ Codelab] タブに移動します。
  2. DevTools を開きます。Windows と Linux の場合は Ctrl+Shift+I、macOS の場合は Cmd+Option+I。
  3. [Application] パネルを選択し、[Service Workers] タブをクリックして、[Update on Reload] チェックボックスをオンにします。このチェックボックスをオンにすると、ページが再読み込みされるたびに Service Worker が強制的に更新されます。

e7d384fb77885b99.png

3. Service Worker を登録する

完成したコード

app ディレクトリに、sw.js という名前の空のファイルがあります。このファイルが Service Worker になります。今のところは空のままで構いません。後でコードを追加します。

まず、このファイルを Service Worker として登録する必要があります。

app/index.html ページは scripts/main.js を読み込みます。この JavaScript ファイルで Service Worker を登録します。

scripts/main.js に次のコードを追加します。

if ('serviceWorker' in navigator && 'PushManager' in window) {
  console.log('Service Worker and Push are supported');

  navigator.serviceWorker.register('sw.js')
  .then(function(swReg) {
    console.log('Service Worker is registered', swReg);

    swRegistration = swReg;
  })
  .catch(function(error) {
    console.error('Service Worker Error', error);
  });
} else {
  console.warn('Push messaging is not supported');
  pushButton.textContent = 'Push Not Supported';
}

このコードは、ブラウザが Service Worker とプッシュ メッセージングをサポートしているかどうかを確認します。サポートされている場合は、sw.js ファイルを登録します。

試してみる

ブラウザの [プッシュ Codelab] タブを更新して、変更を確認します。

Chrome DevTools のコンソールで、次のように Service Worker is registered message を確認します。

5d7ad383d6f235d5.png

アプリケーション サーバーキーを取得する

この Codelab を使用するには、アプリケーション サーバーキーを生成する必要があります。これは、コンパニオン サイト web-push-codelab.glitch.me で行うことができます。

ここで、公開鍵と秘密鍵のペアを生成できます。

push-codelab-04-companion.png

公開鍵を scripts/main.js にコピーして、<Your Public Key> の値を置き換えます。

const applicationServerPublicKey = '<Your Public Key>';

重要: 秘密鍵をウェブアプリに配置しないでください。

4. 状態を初期化する

完成したコード

現時点では、ウェブアプリの [有効にする] ボタンは無効になっており、クリックできません。これは、デフォルトでプッシュボタンを無効にし、ブラウザがプッシュ メッセージングをサポートしていることがわかってから有効にするのが良い方法だからです。また、ユーザーが現在メッセージングに登録しているかどうかを確認することもできます。

scripts/main.js に次の 2 つの関数を作成する必要があります。

  • initializeUI: ユーザーが現在登録しているかどうかを確認します。
  • updateBtn: ユーザーが登録しているかどうかに応じてボタンを有効にし、テキストを変更します。

次のように initializeUI 関数を main.js に追加します。

function initializeUI() {
  // Set the initial subscription value
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    isSubscribed = !(subscription === null);

    if (isSubscribed) {
      console.log('User IS subscribed.');
    } else {
      console.log('User is NOT subscribed.');
    }

    updateBtn();
  });
}

新しいメソッドは、前のステップの swRegistration を使用して、そこから pushManager プロパティを取得し、そのプロパティで getSubscription() を呼び出します。

pushManagergetSubscription() は、現在のサブスクリプションがある場合はそのサブスクリプションで解決される Promise を返します。それ以外の場合は null を返します。これにより、ユーザーがすでに登録しているかどうかを確認し、isSubscribed の値を設定して、updateBtn() を呼び出してボタンを更新できます。

updateBtn() 関数を main.js に追加します。

function updateBtn() {
  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

この関数は、ユーザーが登録しているかどうかに応じてボタンを有効にし、ボタンのテキストを変更します。

最後に、main.js で Service Worker が登録されたときに initializeUI() を呼び出します。

navigator.serviceWorker.register('sw.js')
.then(function(swReg) {
  console.log('Service Worker is registered', swReg);

  swRegistration = swReg;
  initializeUI();
})

試してみる

[プッシュ Codelab] タブを更新します。[プッシュ メッセージングを有効にする] ボタンが有効になり(クリック可能)、コンソールに User is NOT subscribed と表示されます。

a1553f4a0483d227.png

この Codelab の残りの部分を進めていくと、登録または登録解除するたびにボタンのテキストが変わります。

5. ユーザーを登録する

完成したコード

現時点では、[プッシュ メッセージングを有効にする] ボタンはあまり機能しません。これを修正しましょう。

initializeUI() 関数で、ボタンのクリック リスナーを追加します。

function initializeUI() {
  pushButton.addEventListener('click', function() {
    pushButton.disabled = true;
    if (isSubscribed) {
      // TODO: Unsubscribe user
    } else {
      subscribeUser();
    }
  });

  // Set the initial subscription value
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    isSubscribed = !(subscription === null);

    updateSubscriptionOnServer(subscription);

    if (isSubscribed) {
      console.log('User IS subscribed.');
    } else {
      console.log('User is NOT subscribed.');
    }

    updateBtn();
  });
}

ユーザーがボタンをクリックすると、プッシュ メッセージングの登録に時間がかかる場合があるため、ユーザーが 2 回クリックできないようにボタンを無効にします。

ユーザーが現在登録していない場合は、subscribeUser() を呼び出します。これを行うには、次のコードを scripts/main.js に貼り付けます。

function subscribeUser() {
  const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
  swRegistration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: applicationServerKey
  })
  .then(function(subscription) {
    console.log('User is subscribed.');

    updateSubscriptionOnServer(subscription);

    isSubscribed = true;

    updateBtn();
  })
  .catch(function(error) {
    console.error('Failed to subscribe the user: ', error);
    updateBtn();
  });
}

このコードの動作と、ユーザーをプッシュ メッセージングに登録する方法について説明します。

まず、アプリケーション サーバーの公開鍵(Base64 URL セーフ エンコード)を取得し、UInt8Array に変換します。これは、subscribe() 呼び出しの想定される入力です。urlB64ToUint8Array() 関数は scripts/main.js の上部にあります。

値を変換したら、Service Worker の pushManagersubscribe() メソッドを呼び出し、アプリケーション サーバーの公開鍵と値 userVisibleOnly: true を渡します。

const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})

userVisibleOnly パラメータは、プッシュ メッセージが送信されるたびに通知を表示することを保証します。現在、この値は必須であり、true にする必要があります。

subscribe() を呼び出すと、次の手順の後に解決される Promise が返されます。

  1. ユーザーが通知を表示する権限を付与した。
  2. ブラウザがプッシュ サービスにネットワーク リクエストを送信して、PushSubscription の生成に必要なデータを取得した。

これらの手順が成功すると、subscribe() Promise は PushSubscription で解決されます。ユーザーが権限を付与しない場合や、ユーザーの登録に問題がある場合は、Promise はエラーで拒否されます。これにより、Codelab で次の Promise チェーンが生成されます。

swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})
.then(function(subscription) {
  console.log('User is subscribed.');

  updateSubscriptionOnServer(subscription);

  isSubscribed = true;

  updateBtn();

})
.catch(function(err) {
  console.log('Failed to subscribe the user: ', err);
  updateBtn();
});

これにより、サブスクリプションを取得してユーザーを登録済みとして扱うか、エラーをキャッチしてコンソールに記録します。どちらの場合も、updateBtn() を呼び出して、ボタンが再度有効になり、適切なテキストが表示されるようにします。

実際のアプリケーションでは、updateSubscriptionOnServer() 関数でサブスクリプション データをバックエンドに送信しますが、この Codelab では UI にサブスクリプションを表示するだけです。次の関数を scripts/main.js に追加します。

function updateSubscriptionOnServer(subscription) {
  // TODO: Send subscription to application server

  const subscriptionJson = document.querySelector('.js-subscription-json');
  const subscriptionDetails =
    document.querySelector('.js-subscription-details');

  if (subscription) {
    subscriptionJson.textContent = JSON.stringify(subscription);
    subscriptionDetails.classList.remove('is-invisible');
  } else {
    subscriptionDetails.classList.add('is-invisible');
  }
}

試してみる

[プッシュ Codelab] タブに移動し、ページを更新してボタンをクリックします。次のような権限プロンプトが表示されます。

fcc61267a0194e81.png

権限を付与すると、コンソールに User is subscribed と記録されます。ボタンのテキストが [Disable Push Messaging] に変わり、ページの下部にサブスクリプションが JSON データとして表示されます。

5c5505f2ead037c.png

6. 権限の拒否を処理する

完成したコード

まだ処理していないことの 1 つに、ユーザーが権限のリクエストをブロックした場合の処理があります。ユーザーが権限をブロックすると、ウェブアプリは権限プロンプトを再表示できず、ユーザーを登録できなくなるため、特別な考慮が必要です。少なくともプッシュボタンを無効にして、使用できないことをユーザーに知らせる必要があります。

このシナリオを処理する場所は、updateBtn() 関数です。必要なのは、次のように Notification.permission の値を確認することだけです。

function updateBtn() {
  if (Notification.permission === 'denied') {
    pushButton.textContent = 'Push Messaging Blocked';
    pushButton.disabled = true;
    updateSubscriptionOnServer(null);
    return;
  }

  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

権限が denied の場合、ユーザーを登録することはできず、これ以上何もできないため、ボタンを完全に無効にするのが最善の方法です。

試してみる

前のステップでウェブアプリの権限をすでに付与しているため、URL バーの円形の [i] をクリックし、[通知] 権限を [グローバル デフォルトを使用(確認)] に変更する必要があります。

54495592074f10ae.png

この設定を変更したら、ページを更新して [プッシュ メッセージングを有効にする] ボタンをクリックし、権限ダイアログで [ブロック] を選択します。ボタンが無効になり、[プッシュ メッセージングがブロックされました] というテキストが表示されます。

d4cf22468f6defda.png

この変更により、考えられる権限シナリオに対応し、ユーザーを登録できるようになりました。

7. プッシュ イベントを処理する

完成したコード

バックエンドからプッシュ メッセージを送信する方法を学ぶ前に、登録ユーザーがプッシュ メッセージを受信したときに実際に何が起こるかを検討する必要があります。

プッシュ メッセージをトリガーすると、ブラウザはプッシュ メッセージを受信し、プッシュの対象となる Service Worker を特定して、その Service Worker を起動し、プッシュ イベントをディスパッチします。このイベントをリッスンし、その結果として通知を表示する必要があります。

次のコードを sw.js ファイルに追加します。

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'
  };

  event.waitUntil(self.registration.showNotification(title, options));
});

このコードについて説明します。イベント リスナーを追加して、Service Worker で push イベントをリッスンします。

self.addEventListener('push', ... );

(Web Worker を使用したことがない場合、self はおそらく新しいものです。Service Worker ファイルでは、self は Service Worker 自体を参照します)。

プッシュ メッセージを受信すると、イベント リスナーが呼び出され、Service Worker の registration プロパティで showNotification() を呼び出して通知を作成します。showNotification() には title が必要です。また、options オブジェクトを指定して、本文メッセージ、アイコン、バッジを設定することもできます。(バッジは、執筆時点では Android でのみ使用されます)。

const title = 'Push Codelab';
const options = {
  body: 'Yay it works.',
  icon: 'images/icon.png',
  badge: 'images/badge.png'
};
self.registration.showNotification(title, options);

push イベント処理で最後に説明するのは event.waitUntil() です。このメソッドは、渡された Promise が解決されるまで、ブラウザが Service Worker を有効にして実行し続けるようにする Promise を受け取ります。

上記のコードを少しわかりやすくするために、次のように書き換えることができます。

const notificationPromise = self.registration.showNotification(title, options);
event.waitUntil(notificationPromise);

プッシュ イベントについて説明したので、プッシュ イベントをテストしてみましょう。

試してみる

Service Worker でプッシュ イベント処理を行うと、偽のプッシュ イベントをトリガーして、メッセージを受信したときに何が起こるかをテストできます。

ウェブアプリでプッシュ メッセージングに登録し、コンソールに User IS subscribed と表示されることを確認します。DevTools の [Application] パネルの [Service Workers] タブで、[Push] ボタンをクリックします。

1ee499267eeccd1c.png

[Push] をクリックすると、次のような通知が表示されます。

379105dfb0ea56d8.png

注: この手順がうまくいかない場合は、DevTools の [Application] パネルの [Unregister] リンクを使用して Service Worker の登録を解除し、Service Worker が停止するまで待ってから、ページを再読み込みしてください。

8. 通知のクリック

完成したコード

これらの通知のいずれかをクリックしても、何も起こりません。Service Worker で notificationclick イベントをリッスンすることで、通知のクリックを処理できます。

まず、sw.jsnotificationclick リスナーを追加します。

self.addEventListener('notificationclick', function(event) {
  console.log('[Service Worker] Notification click received.');

  event.notification.close();

  event.waitUntil(
    clients.openWindow('https://developers.google.com/web')
  );
});

ユーザーが通知をクリックすると、notificationclick イベント リスナーが呼び出されます。

まず、クリックされた通知を閉じます。

event.notification.close();

次に、新しいウィンドウまたはタブが開き、URL https://developers.google.com/web が読み込まれます。これは自由に変更できます。

event.waitUntil(
    clients.openWindow('https://developers.google.com/web/')
  );

event.waitUntil() を使用すると、新しいウィンドウまたはタブが表示される前にブラウザが Service Worker を終了しないようにできます。

試してみる

DevTools でプッシュ メッセージを再度トリガーして、通知をクリックします。通知が閉じ、新しいタブが開きます。

9. プッシュ メッセージを送信する

DevTools を使用してウェブアプリで通知を表示できることと、クリックして通知を閉じる方法について説明しました。次のステップは、実際のプッシュ メッセージを送信することです。

通常、これにはウェブページからバックエンドにサブスクリプションを送信する必要があります。バックエンドは、サブスクリプションのエンドポイントに API 呼び出しを行うことで、プッシュ メッセージをトリガーします。

これはこの Codelab の範囲外ですが、コンパニオン サイト(web-push-codelab.glitch.me)を使用して実際のプッシュ メッセージをトリガーできます。ページの下部にサブスクリプションを貼り付けます。

bb202867962a0249.png

次に、コンパニオン サイトの [Subscription to Send To] テキスト領域に貼り付けます。

a0dd93bc33a9e8cf.png

[Text to Send] に、プッシュ メッセージとともに送信する文字列を追加します。

[Send push message] ボタンをクリックします。

a5e8e89411ec034.png

プッシュ メッセージが届きます。使用したテキストがコンソールに記録されます。

f6815a356d4f9aaa.png

これにより、データの送受信をテストし、その結果として通知を操作できます。

コンパニオン アプリは、web-push ライブラリを使用してメッセージを送信するノードサーバーです。GitHub の web-push-libs 組織を確認して、プッシュ メッセージの送信に使用できるライブラリを確認することをおすすめします。これにより、プッシュ メッセージをトリガーするための詳細の多くが処理されます。

コンパニオン サイトの コードはすべてこちらで確認できます

10. ユーザーの登録を解除する

完成したコード

不足しているのは、プッシュからユーザーの登録を解除する機能です。これを行うには、PushSubscriptionunsubscribe() を呼び出す必要があります。

scripts/main.js ファイルに戻り、initializeUI()pushButton クリック リスナーを次のように変更します。

pushButton.addEventListener('click', function() {
  pushButton.disabled = true;
  if (isSubscribed) {
    unsubscribeUser();
  } else {
    subscribeUser();
  }
});

新しい関数 unsubscribeUser() を呼び出すようになりました。この関数では、現在のサブスクリプションを取得して、そのサブスクリプションで unsubscribe() を呼び出します。次のコードを scripts/main.js に追加します。

function unsubscribeUser() {
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    if (subscription) {
      return subscription.unsubscribe();
    }
  })
  .catch(function(error) {
    console.log('Error unsubscribing', error);
  })
  .then(function() {
    updateSubscriptionOnServer(null);

    console.log('User is unsubscribed.');
    isSubscribed = false;

    updateBtn();
  });
}

この関数について説明します。

まず、getSubscription() を呼び出して現在のサブスクリプションを取得します。

swRegistration.pushManager.getSubscription()

サブスクリプションが存在する場合は PushSubscription で解決される Promise を返し、それ以外の場合は null を返します。サブスクリプションがある場合は、そのサブスクリプションで unsubscribe() を呼び出すと、PushSubscription が無効になります。

swRegistration.pushManager.getSubscription()
.then(function(subscription) {
  if (subscription) {
    // TODO: Tell application server to delete subscription
    return subscription.unsubscribe();
  }
})
.catch(function(error) {
  console.log('Error unsubscribing', error);
})

unsubscribe() を呼び出すと、完了までに時間がかかる場合があるため、Promise が返されます。その Promise を返すことで、チェーン内の次の then()unsubscribe() が完了するまで待機します。unsubscribe() の呼び出しでエラーが発生した場合に備えて、キャッチ ハンドラも追加します。その後、UI を更新できます。

.then(function() {
  updateSubscriptionOnServer(null);

  console.log('User is unsubscribed.');
  isSubscribed = false;

  updateBtn();
})

試してみる

ウェブアプリで [プッシュ メッセージングを有効にする] または [プッシュ メッセージングを無効にする] を押すと、ログにユーザーの登録と登録解除が表示されます。

81a07119235b53da.png

11. 完了

これでこの Codelab は完了です。

この Codelab では、ウェブアプリにプッシュ通知を追加する方法について説明しました。ウェブ通知でできることについて詳しくは、次のドキュメントをご覧ください

サイトにプッシュ通知をデプロイする場合は、GCM を使用する古いブラウザや標準に準拠していないブラウザのサポートを追加することをおすすめします。詳しくはこちらをご覧ください

参考資料

関連するブログ投稿