PWA ドリブン ウィジェットを構築する

さまざまなオペレーティング システムには、ユーザーがコンテンツを読み取り、タスクを実行できるウィジェット ダッシュボードがあります。 これには、Android ホーム画面ウィジェット、macOS ダッシュボード、今日のパネル ウィジェット、Apple Touch Bar、Samsung Daily Cards、ミニ アプリ ウィジェット、スマート ウォッチ アプリコンパニオンなどがあります。

Windows 11 では、ウィジェットがウィジェット ボードに表示され、タスク バーの左側から開きます。

Windows 11 のウィジェット ボード

Windows 11 では、プログレッシブ Web アプリ (PWA) はウィジェットを定義し、更新し、それらの中のユーザー操作を処理できます。

PWA のカスタム ウィジェットを作成する必要がある

既存の PWA は、Microsoft Edge サイドバーと同様に、ウィジェット ダッシュボードにそのまま配置することはできません。 代わりに、現在 Windows 11 Widgets Board であるウィジェット ホストに適したカスタム ウィジェット エクスペリエンスを構築する必要があります。 (今後、他のウィジェット ホストが存在する可能性があります)。Windows 11 ウィジェット ボードでは、HTML と JavaScript ではなくアダプティブ カード テンプレートを使用してウィジェットを構築する必要があるため、ウィジェットはアプリの UI の残りの部分とは別に設計する必要があります。

関連項目:

PWA ドリブン ウィジェットを構築し、Microsoft ストア経由で配信するには、C++/C# コードは必要ありません。 ウィジェットを作成し、パブリック エンドポイントからウィジェットを正常にインストールして実行できたら、 PWABuilder.com を使用してアプリをパッケージ化し、追加のコードを必要とせずにアプリを Microsoft ストアに発送できます。 PWABuilder.com は localhost からのアプリのパッキングをサポートしていないため、ウィジェットをバッキングする PWA をパブリック エンドポイントからインストールできる必要があります。

関連項目:

WinAppSDK をインストールし、開発者モードを有効にする

ローカル コンピューターでウィジェットの開発とテストを有効にするには:

  • WinAppSDK 1.2 をインストールします。

  • Windows 11 で開発者モードを有効にする:

    1. [設定] を開きます。

    2. [ 設定の検索 ] ボックスに「 developer」と入力し、[ 開発者向け機能を使用する] をクリックします。

    3. 開発者モードを有効にする:

      Windows 11 の開発者設定

ウィジェットを定義する

ウィジェットは、 widgets マニフェスト メンバーを使用して、PWA マニフェスト ファイルで定義されます。 このマニフェスト メンバーは、複数のウィジェット定義を含めることができる配列です。

{
  "name": "PWAmp",
  "description": "A music player app",
  "icons": [
    { "src": "img/icon-96.png", "sizes": "96x96" },
    { "src": "img/icon-128.png", "sizes": "128x128" },
    { "src": "img/icon-256.png", "sizes": "256x256" },
    { "src": "img/icon-512.png", "sizes": "512x512" }
  ],
  "widgets": [
    /* widget definitions go here */
  ]
}

widgets配列の各エントリには、次に示すように、いくつかのフィールドが含まれています。

{
  ...
  "widgets": [
    {
      "name": "PWAmp mini player",
      "description": "widget to control the PWAmp music player",
      "tag": "pwamp",
      "template": "pwamp-template",
      "ms_ac_template": "widgets/mini-player-template.json",
      "data": "widgets/mini-player-data.json",
      "type": "application/json",
      "screenshots": [
        {
          "src": "./screenshot-widget.png",
          "sizes": "600x400",
          "label": "The PWAmp mini-player widget"
        }
      ],
      "icons": [
        {
          "src": "./favicon-16.png",
          "sizes": "16x16"
        }
      ],
      "auth": false,
      "update": 86400
    }
  ]
}

上記の例では、音楽プレーヤー アプリケーションによってミニ プレーヤー ウィジェットが定義されています。 Web アプリ マニフェストのウィジェット定義には、次の必須フィールドと省略可能なフィールドがあります。

フィールド 説明 必須
name ユーザーに表示されるウィジェットのタイトル。 はい
short_name 別の短いバージョンの名前。 いいえ
description ウィジェットの動作の説明。 はい
icons ウィジェットに使用するアイコンの配列。 存在しない場合は、代わりに icons マニフェスト メンバーが使用されます。 1024x1024 より大きいアイコンは無視されます。 いいえ
screenshots ウィジェットの外観を示すスクリーンショットの配列。 screenshot マニフェスト メンバーに似ています。 スクリーンショット項目の [ platform ] フィールドでは、 Windowsany の値がサポートされています。 1024 x 1024 ピクセルを超える画像は無視されます。 Windows 11 ウィジェット ボードに固有のスクリーンショット要件については、「ウィジェット ピッカーとの統合」のスクリーンショット画像の要件に関するページを参照してください。 はい
tag PWA サービス ワーカーのウィジェットを参照するために使用される文字列。 はい
template オペレーティング システム ウィジェット ダッシュボードにウィジェットを表示するために使用するテンプレート。 注: このプロパティは現在、情報提供のみを行っており、使用されていません。 以下の ms_ac_template を参照してください。 いいえ
ms_ac_template オペレーティング システム ウィジェット ダッシュボードにウィジェットを表示するために使用するカスタム アダプティブ カード テンプレートの URL。 以下 の「ウィジェット テンプレートを定義する」 を参照してください。 はい
data テンプレートに入力するデータが見つかる URL。 存在する場合、有効な JSON を返すには、この URL が必要です。 いいえ
type ウィジェット データの MIME の種類。 いいえ
auth ウィジェットで認証が必要かどうかを示すブール値。 いいえ
update ウィジェットが更新される頻度 (秒単位)。 サービス ワーカーのコードは、更新を実行する必要があります。ウィジェットは自動的に更新されません。 「実行時にウィジェット インスタンスにアクセスする」を参照してください。 いいえ
multiple ウィジェットの複数のインスタンスを許可するかどうかを示すブール値。 既定値は true です。 いいえ

ウィジェット テンプレートを定義する

ウィジェットを簡単に作成し、さまざまなオペレーティング システム ウィジェット ダッシュボードに適応させるために、テンプレートを使用して表示されます。 次の 2 種類のテンプレートが存在します。

  • template フィールドを使用して名前で定義された汎用テンプレート。
  • カスタム テンプレート。カスタム テンプレート フィールドを使用して URL によって定義されます。

当面は、カスタム アダプティブ カード テンプレートのみがサポートされています。 アダプティブ カードは、一般的で一貫性のある方法で UI コンテンツを交換するために使用できるオープン カード交換形式です。 アダプティブ カードの概要に関するページを参照してください。

Windows 11 でカスタム アダプティブ カード テンプレートを定義するには、Web アプリ マニフェストにあるウィジェット定義の [ ms_ac_template ] フィールドを使用します。 templateは現在使用されていませんが、必須フィールドです。

{
  ...
  "template": "pwamp-template",
  "ms_ac_template": "widgets/mini-player.json",
  ...
}

ms_ac_template フィールドの値は、テンプレート ファイルの有効な URL である必要があります。

アダプティブ カード テンプレートの例を次に示します。

{
  "type": "AdaptiveCard",
  "body": [
    {
      "type": "TextBlock",
      "size": "Medium",
      "text": "Now playing...",
      "horizontalAlignment": "Center"
    },
    {
      "type": "TextBlock",
      "spacing": "Large",
      "weight": "Bolder",
      "horizontalAlignment": "Center",
      "text": "${song}, by ${artist}",
    }
  ],
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.5"
}

詳細については、「 アダプティブ カードテンプレート」を参照してください。

次に、データをテンプレートにバインドする必要があります。

データをテンプレートにバインドする

テンプレートはウィジェットのユーザー インターフェイスを宣言します。 次に、このユーザー インターフェイスにデータが設定されます。

テンプレートにデータをバインドするには、ウィジェット定義の data フィールドを使用します。 このフィールドは、有効な JSON データを返す URL に設定する必要があります。

前のセクションで定義したテンプレートには、songartistの 2 つの変数が含まれています。これはバインド式の構文で囲まれています:${}。 ウィジェット定義の data URL によって返されるデータには、これらの変数の値が含まれている必要があります。

data URL から返される可能性がある内容の例を次に示します。

{
  "song": "I Will Always Love You",
  "artist": "Whitney Houston"
}

ウィジェットアクションを定義する

ウィジェットでユーザーがタスクを実行できるようにする場合は、アクションをサポートするテンプレートを定義します。

カスタム アダプティブ カード テンプレートで定義されているアクションの例を次に示します。

{
  "type": "AdaptiveCard",
  "body": [
    {
      "type": "TextBlock",
      "size": "Medium",
      "text": "Now playing...",
      "horizontalAlignment": "Center"
    },
    {
      "type": "TextBlock",
      "spacing": "Large",
      "weight": "Bolder",
      "horizontalAlignment": "Center",
      "text": "${song}, by ${artist}",
    }
  ],
  "actions": [
    {
      "type": "Action.Execute",
      "title": "Previous",
      "verb": "previous-song"
    },
    {
      "type": "Action.Execute",
      "title": "Next",
      "verb": "next-song"
    }
  ],
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.5"
}

上記の JSON テンプレートの [ verb ] フィールドに注意してください。 これは、サービス ワーカー コードでウィジェット アクションを処理するときに使用されます。 「 ウィジェットアクションを処理する」を参照してください。

実行時にウィジェット インスタンスにアクセスする

ウィジェットにアクセスし、PWA サービス ワーカー コードから更新できます。 実行時にウィジェットにアクセスすると、次のような場合に役立ちます。

サービス ワーカーは、 self.widgets オブジェクトといくつかのウィジェット イベントにアクセスできます。これは、一緒に、変更に対応し、実行時にウィジェットにアクセスするために使用する API を構成します。

次のセクションでは、コード例を示します。 API のリファレンスについては、 サービス ワーカー API リファレンスを参照してください

インストール時にウィジェットをレンダリングする

PWA がインストールされると、アプリがマニフェストで定義するウィジェットはウィジェット ダッシュボードに追加されますが、まだインストールされていません。 ウィジェットは、ユーザーがダッシュボードからウィジェットを追加することを選択した場合にのみインストールされます。

ウィジェットがインストールされると、ウィジェット定義の ms_ac_template フィールドと data フィールドを使用して自動的にレンダリングされません。

ウィジェットをレンダリングするには、サービス ワーカーで widgetinstall イベントをリッスンし、 widgets.updateByTag 関数を使用してウィジェットを更新します。

// Listen to the widgetinstall event.
self.addEventListener("widgetinstall", event => {
  // The widget just got installed, render it using renderWidget.
  // Pass the event.widget object to the function.
  event.waitUntil(renderWidget(event.widget));
});

async function renderWidget(widget) {
  // Get the template and data URLs from the widget definition.
  const templateUrl = widget.definition.msAcTemplate;
  const dataUrl = widget.definition.data;

  // Fetch the template text and data.
  const template = await (await fetch(templateUrl)).text();
  const data = await (await fetch(dataUrl)).text();

  // Render the widget with the template and data.
  await self.widgets.updateByTag(widget.definition.tag, {template, data});
}

サービス ワーカーの更新に関するウィジェットを更新する

PWA でサービス ワーカー コードが変更されると、ブラウザーはその変更を検出し、新しいサービス ワーカーをインストールしてから、後でサービス ワーカーをアクティブ化します。

この場合は、既に実行されている可能性があるウィジェット インスタンスを更新することが重要です。 サービス ワーカー activate イベントが生成される前に、ウィジェットがインストールされている可能性があります。 空のウィジェットが表示されないようにするには、 activate イベントが発生したときにウィジェットを更新します

// Update the widgets to their initial states
// when the service worker is activated.
self.addEventListener("activate", event => {
  event.waitUntil(updateWidgets());
});

async function updateWidgets() {
  // Get the widget that match the tag defined in the web app manifest.
  const widget = await self.widgets.getByTag("pwamp");
  if (!widget) {
    return;
  }

  // Using the widget definition, get the template and data.
  const template = await (await fetch(widget.definition.msAcTemplate)).text();
  const data = await (await fetch(widget.definition.data)).text();

  // Render the widget with the template and data.
  await self.widgets.updateByTag(widget.definition.tag, {template, data});
}

ウィジェットアクションを処理する

ウィジェット テンプレートにアクションが含まれている場合、ユーザーはレンダリングされたウィジェットのボタンをクリックして、それらのアクションを実行できます。 テンプレートでアクションを定義する方法については、「 ウィジェット アクションの定義」を参照してください。

ユーザーがウィジェット アクションを実行すると、PWA サービス ワーカーで widgetclick イベントがトリガーされます。 ユーザー アクションを処理するには、イベントをリッスンします。

self.addEventListener('widgetclick', (event) => {
  switch (event.action) {
    case 'previous-song':
      // Application logic to play the previous song...
      break;
    case 'next-song':
      // Application logic to play the next song...
      break;
  }
});

簡潔にするために、実際のアプリケーション コードは上記のコード スニペットには表示されません。 previous-songまたはnext-songアクションを受信すると、Client.postMessage を使用してアプリにメッセージを送信して、前または次の曲の再生を開始する必要があることをアプリに通知する必要があります。

上記のイベント リスナーに渡されるwidgetEvent オブジェクトの action プロパティは、ウィジェット テンプレートの action.verb フィールドで定義されている文字列と一致します。

widgetclick イベントとそのイベントからアクセスできる情報の詳細については、以下の「Service worker API リファレンス」を参照してください。

アプリケーションの変更に関するウィジェットを更新する

前のセクションでは、特定のウィジェット イベント、ウィジェット アクション、およびサービス ワーカーの更新が発生したときにウィジェットを更新する方法について説明しました。 また、アプリケーションで何かが発生したとき、またはプッシュ通知が発生したとき、または定期的にウィジェットを更新する場合にも役立ちます。

このセクションでは、定期的なバックグラウンド同期 API を使用してウィジェットを定期的に更新する方法について説明します。 定期的なバックグラウンド同期 API の詳細については、「定期的な バックグラウンド同期 API を使用して新しいコンテンツを定期的に取得する」を参照してください。

次のコード スニペットでは、イベント リスナーを使用して、アプリケーション ウィジェットのさまざまなライフサイクル イベントに対応します。 ウィジェットのインストールが検出されると、定期的な同期が登録され、ウィジェットの削除が検出されると、定期的な同期が登録解除されます。

定期的な同期イベントが発生すると、ウィジェット インスタンスは widgets.updateByTag 関数を使用して更新されます。

self.addEventListener("widgetinstall", event => {
  event.waitUntil(onWidgetInstall(event.widget));
});

self.addEventListener("widgetuninstall", event => {
  event.waitUntil(onWidgetUninstall(event.widget));
});

async function onWidgetInstall(widget) {
  // Register a periodic sync, if this wasn't done already.
  // We use the same tag for the sync registration and the widget to
  // avoid registering several periodic syncs for the same widget.
  const tags = await self.registration.periodicSync.getTags();
  if (!tags.includes(widget.definition.tag)) {
    await self.registration.periodicSync.register(widget.definition.tag, {
      minInterval: widget.definition.update
    });
  }

  // And also update the instance.
  await updateWidget(widget);
}

async function onWidgetUninstall(widget) {
  // On uninstall, unregister the periodic sync.
  // If this was the last widget instance, then unregister the periodic sync.
  if (widget.instances.length === 1 && "update" in widget.definition) {
    await self.registration.periodicSync.unregister(widget.definition.tag);
  }
}

// Listen to periodicsync events to update all widget instances
// periodically.
self.addEventListener("periodicsync", async event => {
  const widget = await self.widgets.getByTag(event.tag);

  if (widget && "update" in widget.definition) {
    event.waitUntil(updateWidget(widget));
  }
});

async function updateWidget(widget) {
  // Get the template and data URLs from the widget definition.
  const templateUrl = widget.definition.msAcTemplate;
  const dataUrl = widget.definition.data;

  // Fetch the template text and data.
  const template = await (await fetch(templateUrl)).text();
  const data = await (await fetch(dataUrl)).text();

  // Render the widget with the template and data.
  await self.widgets.updateByTag(widget.definition.tag, {template, data});
}

デモ アプリ

PWAmp は、ウィジェットを定義する音楽プレーヤー PWA デモ アプリケーションです。 PWAmp ウィジェットを使用すると、ユーザーは現在の曲を視覚化し、前の曲または次の曲を再生できます。

  1. まだ完了していない場合は、 WinAppSDK 1.2 をインストールし、Windows 11 で開発者モードを有効にします。

  2. PWAmp に移動し、Windows 11 にアプリをインストールします。

  3. Windows ロゴ キーを押しながら W キーを押して、Windows 11 ウィジェット ボードを開きます。

  4. [ ウィジェットの追加] をクリックして ウィジェット設定 画面を開き、 PWAmp ミニ プレーヤー ウィジェットまでスクロールして追加します。

  5. ウィジェットの 設定画面を 閉じます。 PWAmp ミニ プレーヤーがウィジェット ボードに表示されるようになりました。

PWAmp ウィジェットには、前または次の曲を再生するための現在の曲とボタンが表示されます。

PWAmp デモ アプリの横にある Windows ウィジェット ボード。ウィジェットボードには、PWAmpアプリで再生されている現在の曲を示すPAMPミニプレーヤーウィジェットが含まれています

サービス ワーカー API リファレンス

サービス ワーカー グローバル オブジェクト (または ServiceWorkerGlobalScope) には、次の Promise ベースのメソッドを公開する widgets 属性が含まれています。

メソッド 説明 パラメーター 戻り値
getByTag(tag) タグでウィジェットを取得します。 ウィジェット タグ タグまたはundefinedに一致するウィジェット オブジェクトに解決される Promise。
getByInstanceId(id) インスタンス ID でウィジェットを取得します。 ウィジェット インスタンス ID 対応する ウィジェット オブジェクトまたは undefinedに解決される Promise。
getByHostId(id) ホスト ID でウィジェットを取得します。 ホスト ID そのホスト内にある ウィジェット オブジェクト の配列。
matchAll(options) 一致するオプションを使用してウィジェットを取得します。 widgetOptions オブジェクト options条件に一致するウィジェット オブジェクトの配列に解決される Promise。
updateByInstanceId(id, payload) インスタンス ID でウィジェットを更新します。 インスタンス ID と widgetPayload オブジェクト undefinedまたはErrorに解決する Promise。
updateByTag(tag, payload) タグでウィジェットを更新します。 ウィジェット タグと widgetPayload オブジェクト undefinedまたはErrorに解決する Promise。

サービス ワーカー グローバル オブジェクトでは、次のイベントも定義されます。

  • widgetinstall: ウィジェット ホストがウィジェットをインストールしているときに発生します。
  • widgetuninstall: ウィジェット ホストがウィジェットをアンインストールしているときに発生します。
  • widgetresume: ウィジェット ホストがインストールされているウィジェットのレンダリングを再開したときに発生します。これは、ホストがリソースを保持するためにウィジェットのレンダリングを中断した後に発生する可能性があります。
  • widgetclick: ユーザーがウィジェット アクションの 1 つを実行したときに発生します。

これらのイベントで提供されるオブジェクトの詳細については、以下の widgetEvent オブジェクトwidgetClickEvent オブジェクトに関するページを参照してください。

widget オブジェクト

各ウィジェットは、次のプロパティを含む widget オブジェクトとして表されます。

widgetOptions オブジェクト

matchAll(options)を使用して複数のウィジェットを取得する場合は、返すウィジェットをフィルター処理するためにwidgetOptions オブジェクトが必要です。 widgetOptions オブジェクトには次のプロパティが含まれています。これらはすべて省略可能です。

  • installable: 返されたウィジェットをインストール可能にする必要があるかどうかを示すブール値。
  • installed: 返されたウィジェットがウィジェット ホストにインストールされているかどうかを示すブール値。
  • tag: 返されたウィジェットをタグでフィルター処理するために使用される文字列。
  • instanceId: 返されたウィジェットをインスタンス ID でフィルター処理するために使用される文字列。
  • hostId: 返されたウィジェットをウィジェット ホスト ID でフィルター処理するために使用される文字列。

widgetPayload オブジェクト

ウィジェット インスタンスを作成または更新する場合、サービス ワーカーは、ウィジェットを設定するために必要なテンプレートとデータを送信する必要があります。 テンプレートとデータは ペイロードと呼ばれます。 widgetPayload オブジェクトには、次のプロパティが含まれています。

  • template: ウィジェットのレンダリングに使用するテンプレートを文字列として指定します。 これは、アダプティブ カード テンプレートの文字列化された JSON になります。
  • data: ウィジェット テンプレートで使用するデータを文字列として指定します。 このデータは、JSON データを文字列化できます。

widgetInstance オブジェクト

このオブジェクトは、ウィジェット ホスト内のウィジェットの特定のインスタンスを表し、次のプロパティを含みます。

  • id: インスタンスを参照するために使用される内部 GUID 文字列。
  • host: このインスタンスをインストールしたウィジェット ホストへの内部ポインター。
  • updated: データがインスタンスに最後に送信された時刻を表す Date オブジェクト。
  • payload: このインスタンスに送信された最後のペイロードを表す widgetPayload オブジェクト

widgetDefinition オブジェクト

このオブジェクトは、PWA マニフェスト ファイルにあるウィジェットの元の定義を表します。 このオブジェクトのプロパティは、上記の「 ウィジェットの定義」に記載されているプロパティと一致します。

widgetEvent オブジェクト

このオブジェクトは、 widgetinstallwidgetuninstallwidgetresume型のサービス ワーカー ウィジェット イベントのリスナーに引数として渡されます。

widgetinstallwidgetuninstall、およびwidgetresumeイベントの種類の場合、widgetEvent オブジェクトには次のプロパティがあります。

プロパティ 説明
widget イベントをトリガーしたウィジェット インスタンス。 ウィジェット
instanceId ウィジェット インスタンス ID。 String
hostId ウィジェット ホスト ID。 String

widgetClickEvent オブジェクト

このオブジェクトは、 widgetclick型のサービス ワーカー ウィジェット イベントのリスナーに引数として渡されます。 clients.openWindow()を使用して、widgetclick イベントに応答してアプリのウィンドウを開くことができます。

widgetClickEvent オブジェクトには、次のプロパティがあります。

プロパティ 説明
action ウィジェット テンプレートの [ actions.verb ] フィールドで定義されているように、イベントをトリガーしたアクション。 ウィジェット アクションの定義に関するページを参照してください。 String
widget イベントをトリガーしたウィジェット インスタンス。 widgetInstance
hostId ウィジェット ホスト ID。 String
instanceId ウィジェット インスタンス ID。 String