Web In Play の最新情報

公開日: 2020 年 12 月 2 日

Trusted Web Activity が導入されて以来、Chrome チームは Bubblewrap での使用を容易にしてきました。Google Play の課金システムとの統合などの機能が追加され、ChromeOS などのより多くのプラットフォームで動作できるようになりました。

Bubblewrap と Trusted Web Activity の機能

Bubblewrap を使用すると、プラットフォーム固有のツールの知識がなくても、信頼できるウェブ アクティビティ内で PWA を起動するアプリを作成できます。

簡素化されたセットアップ フロー

これまで、Bubblewrap を使用するには、Java Development Kit と Android SDK を手動で設定する必要がありましたが、どちらもエラーが発生しやすいものでした。ツールは、初めて実行するときに外部依存関係を自動的にダウンロードするようになりました。

必要に応じて、依存関係の既存のインストールを引き続き使用できます。新しい doctor コマンドを使用すると、問題を特定し、構成の修正を推奨できます。この構成は、updateConfig コマンドを使用してコマンドラインから更新できます。

ウィザードの改善

init を使用してプロジェクトを作成するときに、Bubblewrap は Android アプリの生成に必要な情報を必要とします。このツールは、Web アプリ マニフェストから値を抽出し、可能な場合はデフォルト値を提供します。

これらの値は新しいプロジェクトの作成時に変更できますが、以前は各フィールドの意味は明確ではありませんでした。初期化ダイアログを再ビルドし、各入力フィールドの説明と検証を改善しました。

全画面表示と画面の向きのサポートを表示する

場合によっては、アプリで画面のできるだけ多くの部分を使用することがあります。PWA をビルドする場合は、ウェブアプリ マニフェストの display フィールドを fullscreen に設定することで、これを実装します。

Bubblewrap は、ウェブアプリ マニフェストで全画面表示オプションを検出すると、Android 固有の用語で全画面表示(没入モード)でも起動するように Android アプリを構成します。

ウェブアプリ マニフェストの orientation フィールドは、アプリケーションを縦向き、横向き、またはデバイスで現在使用している向きで起動するかどうかを定義します。Bubblewrap がウェブアプリ マニフェスト フィールドを読み取り、Android アプリの作成時にデフォルトとして使用するようにしました。

どちらの構成も、bubblewrap init フローの一環としてカスタマイズできます。

AppBundles の出力

App Bundle は、最終的な APK の生成と署名を Play に委任するアプリの公開形式です。実際には、ストアからアプリをダウンロードするときに、より小さなファイルをユーザーに提供できます。

Bubblewrap は、アプリケーションを App Bundle として app-release-bundle.aab というファイルにパッケージ化します。2021 年現在、ストアで必須であるため、Google Play ストアにアプリを公開する場合は、この形式を使用することをおすすめします。

位置情報の委任

ユーザーは、デバイスにインストールされているアプリが、テクノロジーに関係なく一貫した動作をすることを期待しています。信頼できるウェブ アクティビティ内で使用する場合、GeoLocationの権限をオペレーティング システムに委任できるようになりました。有効にすると、Kotlin または Java でビルドされたアプリと同じダイアログが表示され、同じ場所で権限を管理するためのコントロールが表示されます。

この機能は Bubblewrap を介して追加できます。Android プロジェクトに追加の依存関係が追加されるため、ウェブアプリが位置情報の利用許可を使用している場合にのみ有効にする必要があります。

最適化されたバイナリ

ストレージ容量が限られたデバイスは世界中の特定の地域で一般的であり、そのようなデバイスの所有者は、多くの場合、サイズの小さいアプリを好みます。信頼できるウェブ アクティビティを使用するアプリは小さなバイナリを生成するため、ユーザーの懸念を軽減できます。

Bubblewrap は、必要な Android ライブラリのリストを減らして最適化されており、生成されるバイナリは 800, 000 バイト小さくなっています。実際には、これは以前のバージョンで生成された平均サイズの半分未満です。小さいバイナリを利用するには、最新バージョンの Bubblewrap を使用してアプリを更新するだけです。

既存のアプリを更新する方法

Bubblewrap によって生成されたアプリは、ウェブ アプリケーションと、PWA を開く軽量の Android ラッパーで構成されています。Trusted Web Activity 内で開く PWA は、他のウェブアプリと同じ更新サイクルに従いますが、ネイティブ ラッパーは更新できます。また、更新する必要があります。

最新のバグ修正と機能が含まれる最新バージョンのラッパーを使用しているように、アプリを更新する必要があります。最新バージョンの Bubblewrap がインストールされている場合、update コマンドは既存のプロジェクトに最新バージョンのラッパーを適用します。

npm update -g @bubblewrap/cli
bubblewrap update
bubblewrap build

これらのアプリを更新するもう 1 つの理由は、ウェブ マニフェストの変更がアプリに適用されるようにすることです。これには、新しい merge コマンドを使用します。

bubblewrap merge
bubblewrap update
bubblewrap build

品質基準の更新

Chrome 86 では、Trusted Web Activity の品質基準が変更されました。詳しくは、Trusted Web Activity の使用による PWA の品質基準の変更をご覧ください。

要約すると、アプリがクラッシュしないように、アプリで次のシナリオを処理する必要があります。

  • アプリの起動時にデジタル アセットリンクを確認できない
  • オフライン ネットワーク リソース リクエストで HTTP 200 が返されない
  • アプリケーションで HTTP 404 エラーまたは 5xx エラーが返される。

アプリケーションが デジタル アセット リンクの検証に合格することを確認する以外に、残りのシナリオはサービス ワーカーで処理できます。

self.addEventListener('fetch', event => {
  event.respondWith((async () => {
    try {
      return await fetchAndHandleError(event.request);
    } catch {
      // Failed to load from the network. User is offline or the response
      // has a status code that triggers the Quality Criteria.
      // Try loading from cache.
      const cachedResponse = await caches.match(event.request);
      if (cachedResponse) {
        return cachedResponse;
      }
      // Response was not found on the cache. Send the error / offline
      // page. OFFLINE_PAGE should be pre-cached when the service worker
      // is activated.
      return await caches.match(OFFLINE_PAGE);
    }
  })());
});

async function fetchAndHandleError(request) {
  const cache = await caches.open(RUNTIME_CACHE);
  const response = await fetch(request);

  // Throw an error if the response returns one of the status
  // that trigger the Quality Criteria.
  if (response.status === 404 ||
      response.status >= 500 && response.status < 600) {
    throw new Error(`Server responded with status: ${response.status}`);
  }

  // Cache the response if the request is successful.
  cache.put(request, response.clone());
  return response;
}

Workbox は、ベスト プラクティスを組み込み、サービス ワーカーを使用する際のボイラープレートを削除します。または、Workbox プラグインを使用して、このようなシナリオを処理することを検討してください。

export class FallbackOnErrorPlugin {
  constructor(offlineFallbackUrl, notFoundFallbackUrl, serverErrorFallbackUrl) {
    this.notFoundFallbackUrl = notFoundFallbackUrl;
    this.offlineFallbackUrl = offlineFallbackUrl;
    this.serverErrorFallbackUrl = serverErrorFallbackUrl;
  }

  checkTrustedWebActivityCrash(response) {
    if (response.status === 404 || response.status >= 500 && response.status <= 600) {
      const type = response.status === 404 ? 'E_NOT_FOUND' : 'E_SERVER_ERROR';
      const error = new Error(`Invalid response status (${response.status})`);
      error.type = type;
      throw error;
    }
  }

  // This is called whenever there's a network response,
  // but we want special behavior for 404 and 5**.
  fetchDidSucceed({response}) {
    // Cause a crash if this is a Trusted Web Activity crash.
    this.checkTrustedWebActivityCrash(response);

    // If it's a good response, it can be used as-is.
    return response;
  }

  // This callback is new in Workbox v6, and is triggered whenever
  // an error (including a NetworkError) is thrown when a handler runs.
  handlerDidError(details) {
    let fallbackURL;
    switch (details.error.details.error.type) {
      case 'E_NOT_FOUND': fallbackURL = this.notFoundFallbackUrl; break;
      case 'E_SERVER_ERROR': fallbackURL = this.serverErrorFallbackUrl; break;
      default: fallbackURL = this.offlineFallbackUrl;
    }

    return caches.match(fallbackURL, {
      // Use ignoreSearch as a shortcut to work with precached URLs
      // that have _WB_REVISION parameters.
      ignoreSearch: true,
    });
  }
}