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

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

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

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

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

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

試してみる

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

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

コンセプトと使用方法

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

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

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

ユーザー エンゲージメントを適切に把握する

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

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

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

定期的同期の登録

前述のように、定期的なバックグラウンド同期には 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')を示しています。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 の記録ボタン
Chrome DevTools の記録ボタン

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

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

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

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

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

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

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

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

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

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