Web In Play の最新情報

公開日: 2020 年 12 月 2 日

Trusted Web Activity の導入以降、Chrome チームは Bubblewrap での使いやすさを向上させてきました。Google Play 請求サービスとの統合などの機能が追加され、ChromeOS などのより多くのプラットフォームで動作するようになりました。

Bubblewrap と Trusted Web Activity の機能

Bubblewrap を使用すると、プラットフォーム固有のツールに関する知識がなくても、Trusted Web Activity 内で PWA を起動するアプリを作成できます。

シンプルなセットアップ フロー

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

必要に応じて、依存関係の既存のインストールを使用することもできます。新しい doctor コマンドは、問題の検出と構成の修正の推奨に役立ちます。構成は、updateConfig コマンドを使用してコマンドラインから更新できるようになりました。

ウィザードの改善

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

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

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

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

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

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

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

AppBundles の出力

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

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

位置情報の委任

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

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

最適化されたバイナリ

ストレージが限られたデバイスは、世界の特定の地域で一般的であり、そのようなデバイスの所有者は、サイズの小さいアプリを好む傾向があります。Trusted Web Activity を使用するアプリケーションは、小さなバイナリを生成するため、ユーザーの不安を軽減できます。

Bubblewrap は、必要な Android ライブラリのリストを減らすことで最適化され、生成されるバイナリが 800k 削減されました。実際には、以前のバージョンで生成された平均サイズの半分未満です。バイナリのサイズを小さくするには、最新バージョンの 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 エラーが返される。

アプリが Digital Asset Links の検証に合格することを確認する以外に、残りのシナリオは Service Worker で処理できます。

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,
    });
  }
}