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

ウェブアプリのデータをバックグラウンドで同期して、アプリに近いエクスペリエンスを実現

次のいずれかの状況になったことがありますか。

  • 不安定な、または接続できない電車や地下鉄に乗っている
  • 視聴された動画が多すぎるため、携帯通信会社によってスロットリングされた
  • 帯域幅が需要に追いつくのに苦労している国に住んでいる

もしそうなら、ウェブで特定の処理を行うことにイライラを感じていることでしょう。また、このような状況でプラットフォーム固有のアプリの方がパフォーマンスが向上するのはなぜか、不思議に思ったことでしょう。プラットフォーム固有のアプリは、ニュース記事や天気情報などの最新のコンテンツを事前に取得できます。地下鉄にネットワークがない場合でも、ニュースを読むことができます。

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

試してみる

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

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

コンセプトと使用方法

定期的なバックグラウンド同期により、プログレッシブ ウェブアプリや Service Worker がサポートするページが起動されたときに、最新のコンテンツを表示できます。そのために、アプリやページが使用されていないときに、バックグラウンドでデータをダウンロードします。これにより、アプリの表示中に、起動後にアプリのコンテンツが更新されないようにすることができます。さらに、更新前にアプリにコンテンツ スピナーが表示されないようにすることもできます。

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

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

ユーザー エンゲージメントを適切に行う

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

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

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

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

ブラウザでこの処理が頻繁に、かつ制限なく行われると、プライバシーに関する懸念が生じる可能性があります。Chrome では、このリスクに対する定期的なバックグラウンド同期のリスクへの対処法を紹介します。

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

いつ利用できるようになりますか?

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

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

同期のタイミングはデベロッパーが制御しません。同期頻度はアプリの使用頻度に合わせます。(現時点では、プラットフォーム固有のアプリではこの処理を行いません)。また、デバイスの電源と接続の状態も取得します。

使用場面

Service Worker が復帰して periodicsync イベントを処理した場合、データをリクエストする機会はありますが、データをリクエストする義務はありません。イベントを処理する際は、ネットワーク状態と使用可能なストレージを考慮し、レスポンスで異なる量のデータをダウンロードする必要があります。次のリソースを参考にしてください。

権限

Service Worker をインストールしたら、Permissions API を使用して periodic-background-sync をクエリします。これは、ウィンドウまたは Service Worker のコンテキストから行うことができます。

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() を使用してウェブアプリの設定ページにアクティブな登録のリストを表示し、ユーザーが特定の種類の更新を有効または無効にできるようにすることもできます。

定期的なバックグラウンド同期イベントへの応答

定期的なバックグラウンド同期イベントに応答するには、periodicsync イベント ハンドラを Service Worker に追加します。渡される 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')であることを表しています。updateArticles() の呼び出しは event.waitUntil() でラップされているため、記事がダウンロードおよび保存される前に Service Worker が終了しないようにします。

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

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

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

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

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

イベントのシミュレーション

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

これは、Chrome DevTools の [Application] パネル内の [Service Workers] セクションで行うことができます。[Periodic Sync] フィールドでは、使用するイベントのタグを指定し、必要に応じて何度でもトリガーできます。

[Application] パネルの [Service Workers] セクションには、[Periodic Sync] テキスト フィールドとボタンが表示されています。

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

Chrome 81 以降では、DevTools の [アプリケーション] パネルに [定期的なバックグラウンド同期] セクションが表示されます。

[Periodic Background Sync] セクションが表示されている [Application] パネル