見出し画像

iOS 16.4+ で Notification API を使って通知する最低構成

当記事の140字以下のまとめは以下


プッシュ通知ではない

今回扱うテーマはプッシュ通知ではない。つまり、アプリ側で完全任意のタイミングで送信できる類いのモノではない。

あくまでWebアプリ (Webページ) から iOS の通知を鳴らすための最小構成を残す。


プッシュ通知を実装したい場合、Notification API と Push API を組み合わせる必要がある。公式ドキュメントはここらへん。


なお、未検証だが Service Worker と setTimeout などを組み合わせることで「30分後に通知を鳴らす」などのアクションを予約できる可能性はある。Webアプリ上のユーザーアクションが起点となるため、いずれにしろプッシュ通知と呼ぶには違和感がある。


前提

  • Webアプリをインストール可能にしている

  • インストールされたWebアプリを開いている

  • Service Workers が動いている (Controlled SW がある)

    • /sw.js などの配置

    • JS で navigator.serviceWorker.register("/sw.js") の実行、および activated が済んでいる


通知処理

const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
  await registration.showNotification(title, options);
}
// navigator.serviceWorker.ready.then((registration) => registration.showNotification(title, options))
}

他ブラウザで動く Notification コンストラクタでは、エラーにこそならないが通知されない。ここで躓いているネット記事がいくつかあった。

new Notification(title, options)


インストールの壁をどのように越えるかが問題

iOS Safari であれば、以下の手順を取る必要がある

  1. 追加したいWebアプリを開く

  2. 「共有メニュー」を押下

  3. 「ホーム画面に追加」を押下

  4. 「追加設定」で各種設定後に確定


以前作ったインストールボタンも、挙動が完全に制御できておらず動かないパターンを観測している(恐らくイベント処理が甘い)

const InstallationButton = ({
  message = "ホーム画面に追加",
  disabledMessage = "ホーム画面に追加済み",
}: Props) => {
  const [deferredPrompt, setDeferredPrompt] =
    useState<BeforeInstallPromptEvent | null>(null);
  const [isInstallable, setIsInstallable] = useState(false);

  useEffect(() => {
    window.addEventListener("beforeinstallprompt", (e: Event) => {
      setDeferredPrompt(e as BeforeInstallPromptEvent);
      setIsInstallable(true);
    });
  }, []);

  const onInstallClick = async () => {
    if (!deferredPrompt) {
      return;
    }

    deferredPrompt.prompt();
    const choiceResult = await deferredPrompt.userChoice;
    if (choiceResult.outcome === "accepted") {
      setDeferredPrompt(null);
      setIsInstallable(false);
    } else {
      setIsInstallable(true);
    }
  };

  return (
    <button
      onClick={() => void onInstallClick()}
      className="rounded-full bg-white/10 px-10 py-3 font-semibold text-white no-underline transition hover:bg-white/20"
      disabled={!isInstallable}
    >
      {isInstallable ? message : disabledMessage}
    </button>
  );
};

現状、iOS における Notification API の利用にはどうにかしてインストールしてもらう必要がある。インストールボタンはその方法の一つとして検討の余地がある


注意点

Controlled な Service Workers が必要な点は、アプリ側で制御が雑多な場合があるので注意しなければならない。


現状、私のアプリは開発向けだけなので以下のように強制的に Controlled に持って行っているが、本番になったら見直す (大丈夫か再検討する) 必要がある。参考までに残しておく

// /sw.js

self.addEventListener("install", (event) => {
  console.log("Installing Service Worker...", new Date().toLocaleTimeString());
  event.waitUntil(self.skipWaiting());
});
self.addEventListener("activate", (event) => {
  console.log("Activating Service Worker.", new Date().toLocaleTimeString());
  event.waitUntil(clients.claim());
});


この記事が気に入ったらサポートをしてみませんか?