Periodic Background Sync API によるオフライン エクスペリエンスの拡充

ウェブアプリのデータをバックグラウンドで同期して、アプリのようなエクスペリエンスを実現する

次のような状況になったことはございますか?

  • 電車や地下鉄に乗っていて、接続が不安定または接続されていない
  • 動画を視聴しすぎたため、携帯通信会社によって通信速度が制限されている
  • 帯域幅が需要に追いつかない国に居住している

すでにウェブで特定の作業をこなすことに不満を感じている方もいるでしょう。また、プラットフォーム固有のアプリがこのようなシナリオでうまく機能する理由を不思議に思っていることでしょう。プラットフォーム固有のアプリは、ニュース記事や天気情報などの最新のコンテンツを事前に取得できます。地下鉄でネットワークがなくても、ニュースを読むことができます。

定期的なバックグラウンド同期を使用すると、ウェブ アプリケーションはバックグラウンドでデータを定期的に同期できるため、ウェブアプリの動作がプラットフォーム固有のアプリの動作に近づきます。

試してみる

定期的なバックグラウンド同期は、ライブデモアプリで試すことができます。使用前に、次の点を確認してください。

  • Chrome 80 以降を使用している。
  • 定期的なバックグラウンド同期を有効にする前に、ウェブアプリをインストールします。

コンセプトと使用方法

定期的なバックグラウンド同期により、プログレッシブ ウェブアプリや Service Worker を利用するページの起動時に最新のコンテンツを表示できます。アプリやページが使用されていないときにバックグラウンドでデータをダウンロードすることで、この処理が行われます。これにより、アプリのコンテンツが起動後に更新され、表示中に更新されなくなります。さらに、更新前にアプリがコンテンツ スピナーを表示するのを防ぐことができます。

定期的なバックグラウンド同期がない場合、ウェブアプリは別の方法でデータをダウンロードする必要があります。一般的な例としては、プッシュ通知を使用して Service Worker を復帰させます。「新しいデータが利用可能」などのメッセージが表示されて、ユーザーの操作が中断される。データの更新は基本的に副作用です。ただし、重要なニュース速報など、本当に重要な最新情報については、引き続きプッシュ通知を使用できます。

定期的なバックグラウンド同期は、バックグラウンド同期と混同されがちです。名前は似ていますが、ユースケースは異なります。バックグラウンド同期は、以前のリクエストが失敗したときにサーバーにデータを再送信するために最もよく使用されます。

ユーザー エンゲージメントを高める

定期的なバックグラウンド同期を誤って行うと、ユーザーのリソースを浪費する可能性があります。Chrome では、リリース前にトライアル期間を設け、問題がないことを確認しました。このセクションでは、この機能を可能な限り便利にするために Chrome が行った設計上の決定について説明します。

Chrome で最初に行われた設計上の決定は、ウェブアプリは、ユーザーがデバイスにインストールし、個別のアプリとして起動した後にのみ、定期的なバックグラウンド同期を使用できることです。定期的なバックグラウンド同期は、Chrome の通常のタブのコンテキストでは使用できません。

さらに、Chrome では、使用されていないウェブアプリや使用頻度の低いウェブアプリがバッテリーやデータを不必要に消費しないようにするため、定期的なバックグラウンド同期をデベロッパーがユーザーに価値を提供することで獲得できるように設計しています。具体的には、Chrome はサイト エンゲージメント スコアabout://site-engagement/)を使用して、特定のウェブアプリで定期的なバックグラウンド同期が発生するかどうかと、その頻度を判断します。つまり、エンゲージメント スコアがゼロより大きい場合を除き、periodicsync イベントはまったく発生しません。また、その値は periodicsync イベントが発生する頻度に影響します。これにより、バックグラウンドで同期されるアプリは、アクティブに使用しているアプリのみになります。

定期的なバックグラウンド同期は、一般的なプラットフォームの既存の API や手法と類似しています。たとえば、1 回限りのバックグラウンド同期やプッシュ通知を使用すると、ユーザーがページを閉じた後も、ウェブアプリのロジックを(サービス ワーカーを介して)少し長く維持できます。ほとんどのプラットフォームでは、重要なアップデート、コンテンツのプリフェッチ、データの同期などのユーザー エクスペリエンスを向上させるために、バックグラウンドで定期的にネットワークにアクセスするアプリがインストールされていることが一般的です。同様に、定期的なバックグラウンド同期により、ウェブアプリのロジックの存続期間が延長され、定期的に数分間実行されるようになります。

ブラウザでこの処理が頻繁に制限なく行われると、プライバシーに関する懸念が生じる可能性があります。定期的なバックグラウンド同期のリスクに どのように対処しているかを以下に示します。

  • バックグラウンド同期アクティビティは、デバイスが以前に接続していたネットワーク上でのみ発生します。信頼できる事業者が運営するネットワークにのみ接続することをおすすめします。
  • すべてのインターネット通信と同様に、定期的なバックグラウンド同期では、クライアントの IP アドレス、通信先のサーバー、サーバーの名が明らかになります。この露出を、アプリがフォアグラウンドでのみ同期した場合とほぼ同じになるように、ブラウザは、ユーザーがそのアプリを使用する頻度に合わせてアプリのバックグラウンド同期の頻度を制限します。ユーザーがアプリを頻繁に操作しなくなると、定期的なバックグラウンド同期のトリガーが停止します。これは、プラットフォーム固有のアプリの現状よりも大幅な改善です。

使用できる状況

使用ルールはブラウザによって異なります。上記をまとめると、Chrome では定期的なバックグラウンド同期に次の要件が適用されます。

  • 特定のユーザー エンゲージメント スコア。
  • 以前に使用したネットワークの存在。

同期のタイミングはデベロッパーが制御できません。同期頻度は、アプリの使用頻度に合わせて調整します。(現在のところ、プラットフォーム固有のアプリではこの処理は行われません)。また、デバイスの電源と接続状態も考慮されます。

使用に適したケース

サービス ワーカーが起動して periodicsync イベントを処理するときに、データをリクエストする機会はありますが、リクエストする義務はありません。イベントを処理する際は、ネットワークの状態と使用可能なストレージを考慮し、応答として異なる量のデータをダウンロードする必要があります。以下のリソースを参考にしてください。

権限

サービス ワーカーをインストールしたら、Permissions API を使用して periodic-background-sync をクエリします。これは、ウィンドウまたはサービス ワーカーのコンテキストから行えます。

const status = await navigator.permissions.query({
  name: 'periodic-background-sync',
});
if (status.state === 'granted') {
  // Periodic background sync can be used.
} else {
  // Periodic background sync cannot be used.
}

定期的同期の登録

前述のように、定期的なバックグラウンド同期には Service Worker が必要です。ServiceWorkerRegistration.periodicSync を使用して PeriodicSyncManager を取得し、register() を呼び出します。登録には、タグと最小同期間隔(minInterval)の両方が必要です。タグは、登録された同期を識別するため、複数の同期を登録できます。以下の例では、タグ名は 'content-sync'minInterval は 1 日です。

const registration = await navigator.serviceWorker.ready;
if ('periodicSync' in registration) {
  try {
    await registration.periodicSync.register('content-sync', {
      // An interval of one day.
      minInterval: 24 * 60 * 60 * 1000,
    });
  } catch (error) {
    // Periodic background sync cannot be used.
  }
}

登録の確認

periodicSync.getTags() を呼び出して、登録タグの配列を取得します。以下の例では、タグ名を使用して、キャッシュの更新が有効であることを確認して、再度更新されないようにしています。

const registration = await navigator.serviceWorker.ready;
if ('periodicSync' in registration) {
  const tags = await registration.periodicSync.getTags();
  // Only update content if sync isn't set up.
  if (!tags.includes('content-sync')) {
    updateContentOnPageLoad();
  }
} else {
  // If periodic background sync isn't supported, always update.
  updateContentOnPageLoad();
}

getTags() を使用して、ウェブアプリの設定ページに有効な登録のリストが表示されるようにし、ユーザーが特定のタイプの更新を有効または無効にできるようにすることもできます。

定期的なバックグラウンド同期イベントに応答する

定期的なバックグラウンド同期イベントに応答するには、Service Worker に periodicsync イベント ハンドラを追加します。渡される event オブジェクトには、登録時に使用された値に一致する tag パラメータが含まれます。たとえば、定期的なバックグラウンド同期が 'content-sync' という名前で登録されている場合、event.tag'content-sync' になります。

self.addEventListener('periodicsync', (event) => {
  if (event.tag === 'content-sync') {
    // See the "Think before you sync" section for
    // checks you could perform before syncing.
    event.waitUntil(syncContent());
  }
  // Other logic for different tags as needed.
});

同期の登録解除

登録済みの同期を終了するには、登録を解除する同期の名前を指定して periodicSync.unregister() を呼び出します。

const registration = await navigator.serviceWorker.ready;
if ('periodicSync' in registration) {
  await registration.periodicSync.unregister('content-sync');
}

インターフェース

Periodic Background Sync API が提供するインターフェースについて簡単に説明します。

  • PeriodicSyncEvent。ブラウザの選択時に ServiceWorkerGlobalScope.onperiodicsync イベント ハンドラに渡されます。
  • PeriodicSyncManager。定期的な同期の登録と登録解除を行い、登録された同期のタグを提供します。ServiceWorkerRegistration.periodicSync プロパティからこのクラスのインスタンスを取得します。
  • ServiceWorkerGlobalScope.onperiodicsyncPeriodicSyncEvent を受信するハンドラを登録します。
  • ServiceWorkerRegistration.periodicSyncPeriodicSyncManager への参照を返します。

コンテンツの更新

次の例では、定期的なバックグラウンド同期を使用して、ニュース サイトまたはブログの最新記事をダウンロードしてキャッシュに保存します。タグ名は、同期の種類('update-articles')を示します。記事のダウンロードと保存前に Service Worker が終了しないように、updateArticles() の呼び出しは event.waitUntil() でラップされています。

async function updateArticles() {
  const articlesCache = await caches.open('articles');
  await articlesCache.add('/api/articles');
}

self.addEventListener('periodicsync', (event) => {
  if (event.tag === 'update-articles') {
    event.waitUntil(updateArticles());
  }
});

既存のウェブアプリに定期的なバックグラウンド同期を追加する

既存の PWA に定期的なバックグラウンド同期を追加するには、この一連の変更が必要でした。この例には、ウェブアプリの定期的なバックグラウンド同期の状態を記述する便利なロギング ステートメントがいくつか含まれています。

デバッグ

ローカルでテストしている間に、定期的なバックグラウンド同期のエンドツーエンドのビューを取得するのは難しい場合があります。アクティブな登録、同期間隔の概算、過去の同期イベントのログに関する情報は、ウェブアプリの動作をデバッグする際に貴重なコンテキストを提供します。幸い、Chrome DevTools の試験運用版機能で、これらの情報をすべて確認できます。

ローカル アクティビティの記録

DevTools の [定期的なバックグラウンド同期] セクションは、定期的なバックグラウンド同期のライフサイクルの主要なイベント(同期の登録、バックグラウンド同期の実行、登録解除)を中心に構成されています。これらのイベントに関する情報を取得するには、[録画を開始] をクリックします。

DevTools の記録ボタン
DevTools の記録ボタン

記録中、イベントに対応するエントリが DevTools に表示され、それぞれにコンテキストとメタデータが記録されます。

記録された定期的なバックグラウンド同期データの例
記録された定期的なバックグラウンド同期データの例

一度有効にすると、最大 3 日間有効なままになります。これにより、DevTools は、数時間後でも発生する可能性があるバックグラウンド同期に関するローカル デバッグ情報をキャプチャできます。

イベントをシミュレートする

バックグラウンド アクティビティの記録は便利ですが、通常の頻度でイベントが発生するのを待たずに、periodicsync ハンドラをすぐにテストしたい場合があります。

これは、Chrome DevTools の [Application] パネルの [Service Workers] セクションで確認できます。[定期的同期] フィールドでは、使用するイベントのタグを指定し、イベントを任意の数だけトリガーできます。

[アプリケーション] パネルの [Service Workers] セクションに、[定期同期] テキスト フィールドとボタンが表示されます。

DevTools インターフェースの使用

Chrome 81 以降では、DevTools の [Application] パネルに [Periodic Background Sync] セクションが表示されます。

[定期的なバックグラウンド同期] セクションが表示された [アプリケーション] パネル