May 25, 2020 • ☕️ 5 min read

プッシュ通知を聞いたら思い出すのがネイティブAPPでしょう。でも、ServiceWorkerを通じ、WebAPPでもプッシュ通知ができるとご存知ですか?

ServiceWorkerはPWA(Progressive Web Apps)の不可欠なツールで、ServiceWorkerを使うことで、バックエンドでデータの同期、キャッシュなど、WebAPPをオフライン状態でも動くネイティブAPPのようなユーザ体験を提供するができます。

さらに、ネイティブAPPと同じように、プッシュ通知もできます。しかし、Webプッシュ通知はFCM(Firebase Cloud Messaging))みたいな送信用のエンドポイントが必要となります。すなわち、Webプッシュ通知にはサーバーへの通信オンラインが必要です。

しかし、スケジュール通りに通知するのがオンライン、オフラインと関わらず、全ての環境に使えることが望ましいです。例えば、「お薬の時間です」「ベランダの野菜に肥料の日です」など、インタネットの環境がなくても通知するのが重要でしょう。

それを実現するために、Webプッシュ通知のスケジュール機能が現れました。現時点(2020.05.25)まだ試験中なんですが、近くの未来にリリースされるでしょう。

今日はWebプッシュ通知のスケジューラーを実装してみましょう*。

※デスクトップのChromeのみ動く

準備

  1. Chromeの#enable-experimental-web-platform-features を有効へ

    chrome://flags/を開いて、#enable-experimental-web-platform-featuresを検索して有効にしてください。

    enable-experimental-web-platform-features.png

  2. ServiceWorker用のhttps環境の構築

    Macなら簡単ですが、Windowsならちょっと煩雑となります。Windowsなら5分でWindowsにhttps://localhost:8080を立ち上げるを読んでみてください。

フォルダ構成

/project
├── index.html
├── app.js
├── sw.js

ServiceWorkerを登録

ServiceWorkerの利用可能かチェックします。

index.html
if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register('./sw.js');
}

ServiceWorkerをインストール

sw.js
self.addEventListener("install", () => {
  console.log("SW installed!");
});

いきなりプッシュ通知のスケジューラーの実装

app.js
/**
 * todoTime: UNIXエポック*から経過したミリ秒
 */
async function registerTodo({todoName, todoTime, title = "Todo"}) {
  const reg = await navigator.serviceWorker.getRegistration();
  Notification.requestPermission().then((permission) => {
    if (permission !== "granted") {
      // プッシュ通知許可されない場合エラー
      alert("You have to allow push notifications!");
      return;
    }

    // プッシュ通知の登録
    reg.showNotification(title, {
      tag: todoTime,
      body: todoName,
      showTrigger: new TimestampTrigger(todoTime),
      data: {
        url: window.location.href,
      },
    });
  });
}

わかりやすいでしょう。キーポイントはTimestampTriggerです。プッシュ通知の希望時刻を登録すれば、その時刻で通知が来ます。

過ぎた時刻を登録すると、プッシュ通知が待たずにすぐ表示されます。

プッシュ通知が来た時のスクリーンショット:

push-notification.png

プッシュ通知押下時の処理

下記の機能を実装すれば親切でしょう。

  • プッシュ通知がクリックされるとき、1つ目のタブへfocusし、現在のスケジュールを表示させる
  • WebAPPが閉じられた場合、Chromeに新らしいタブに自動開く
app.js
self.addEventListener("notificationclick", (event) => {
  event.waitUntil(
    self.clients.matchAll().then((clients) => {
      if (clients.length === 0) {
        // WebAppが存在しない場合、新しいタブへ
        self.clients.openWindow("/");
      } else {
        // 複数タブの場合、1つ目のタブへfocus
        clients[0].focus();
      }
    })
  );
});

テスト

index.html
<button type="button" class="btn btn-primary" onclick="addTodoWithPushNotification()">Add Todo</button>
app.js
function addTodoWithPushNotification() {
  registerTodo("It's time to eat your pills!", new Date().getTime() + 5000, "Doctor's hint");
}

ボタンをクリックすると、5秒後通知が来るはずです。タイトルはDoctor’s hint、内容はIt’s time to eat your pills!となります。

完了

実装は以上となります。意外に簡単でしょう。興味があれば、サンプルプロジェクトで試してみましょう。

ソースコード:https://github.com/thundermiracle/try-scheduled-push

サンプルページ:https://thundermiracle.github.io/try-scheduled-push/


Blog part of ThunderMiracle.com