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

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

次のいずれかの状況に陥ったことはありますか?

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

そうした経験があるなら、ウェブで特定のタスクを完了する際の不満を感じたことがあるでしょう。また、このようなシナリオでプラットフォーム固有のアプリが優れていることが多いのはなぜだろうと思ったことがあるでしょう。プラットフォーム固有のアプリは、ニュース記事や天気情報などの最新のコンテンツを事前に取得できます。地下鉄でネットワークに接続できない場合でも、ニュースを読むことができます。

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

試してみる

DevTools Tips は、Periodic Background Sync API を使用する PWA です。DevTools Tips PWA は、新しいデベロッパー ツールのヒントを毎日取得してキャッシュに保存します。これにより、ユーザーはオンラインかどうかにかかわらず、次回アプリを開いたときにヒントにアクセスできます。Periodic Background Sync API を利用するには、必ずアプリをインストールしてください。

GitHub のソースコードに移動します。特に、アプリは registerPeriodicSync() 関数で定期的な同期を登録します。サービス ワーカー コードは、アプリが periodicsync イベントをリッスンする場所です。

コンセプトと使用方法

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

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

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

ユーザー エンゲージメントを正しく把握する

定期的なバックグラウンド同期を適切に実装しないと、ユーザーのリソースが無駄になる可能性があります。リリース前に、Chrome は試験運用期間を設けて、それが正しいことを確認しました。このセクションでは、この機能をできるだけ便利にするために Chrome が行った設計上の判断について説明します。

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

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

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

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

  • バックグラウンド同期アクティビティは、デバイスが以前に接続したネットワークでのみ発生します。信頼できる組織が運営するネットワークにのみ接続することをおすすめします。
  • すべてのインターネット通信と同様に、定期的なバックグラウンド同期では、クライアントの 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.
}

定期的な同期を登録する

すでに述べたように、定期的なバックグラウンド同期にはサービス ワーカーが必要です。ServiceWorkerRegistration.periodicSync を使用して PeriodicSyncManager を取得し、その PeriodicSyncManagerregister() を呼び出します。登録には、タグと最小同期間隔(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 イベント ハンドラを追加します。渡される 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 への参照を返します。

以降のセクションでは、Periodic Background Sync API の使用例を示します。

コンテンツを更新する

次の例では、定期的なバックグラウンド同期を使用して、ニュース サイトやブログの最新の記事をダウンロードしてキャッシュに保存します。タグ名に注目してください。これは、同期の種類('update-articles')を示しています。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 に定期的なバックグラウンド同期を追加するには、この一連の変更が必要でした。この例には、ウェブアプリの定期的なバックグラウンド同期の状態を説明する、役立つロギング ステートメントがいくつか含まれています。

Periodic Background Sync API をデバッグする

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

ローカル アクティビティを記録する

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

DevTools の録画ボタン
DevTools の録画ボタン

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

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

一度録画を有効にすると、最長 3 日間有効な状態が維持され、DevTools で、数時間後に発生する可能性のあるバックグラウンド同期に関するローカル デバッグ情報をキャプチャできます。

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

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

これを行うには、Chrome DevTools の [Application] パネルにある [Service Workers] セクションを使用します。[Periodic Sync] フィールドでは、使用するイベントのタグを指定し、必要な回数だけトリガーできます。

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

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

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

[アプリケーション] パネルの [定期的なバックグラウンド同期] セクション
Periodic Background Sync セクション