Open5

Widgetについて

かむいかむい

HIG: Widgetより抜粋

  • Widgetとは
    • アプリやゲームのタイムリーで関連性の高い情報を少量表示し、追加コンテキストで一目で確認できるようにしたもの
    • アプリを開くことなく特定の機能をロック, ホーム画面に表示し、必要な情報に素早くアクセスできる
    • Widgetを編集しパーソナライズさせることもできる
  • ポイント
    • 驚きと喜びを与える機会を探させる
      • 誕生日にレイアウトを変えたり、意味のあるものを提供
    • 表示するのはシンプルな1機能であること
      • ユーザーにとって最も関心の高いものを抽出
    • 大きいサイズには多く情報を載せることができるが、目的を見失わないように
      • 逆に小さいサイズで表示したものを大きいサイズでもただ拡大するのだけは避けること
        • 表示したいコンテンツに最適なサイズを選ぶことが大事
    • 素早く情報にアクセスできること
      • アプリのアイコンと同じような扱いになっては誰も使わない
    • 動的にViewを更新しコンテンツを新鮮に保つこと
    • 認証が必要で見れない場合はその旨もWidget上で伝えるようにする
    • Viewの更新をうまく伝えるために最大2秒のアニメーションを実施するのもアリ
  • 注意
    • Widgetはリアルタイムの更新をサポートしていない
      • 頻繁にリアルタイムに情報を更新する機能を提供したい場合はLive Activityを利用
    • システム側の都合で更新の制限を調整することがある
    • 適切な更新頻度をTimelineを使い設定する必要がある
かむいかむい

WidgetKitより抜粋

  • WidgetKitを使うことでWidget機能をアプリで利用することができる
  • ユーザーに最新の状態をひと目で把握できるために、最新の状態を維持する必要がある
  • 詳細情報が必要な場合、タップしてアプリ内の該当の場所に遷移させる
  • サイズを選択し、幅広い情報をWidgetに表示できる
    • ユーザーはニーズにあう表示に調整, 配置ができる
  • Widgetをスタックするスマートローテーションを有効化
    • WidgetKitは関連性のあるWidgetをスタックの一番上に自動的に配置
      • ユーザーが適切なタイミングで重要な詳細情報を確認できる
  • 実装
    • Widget Extensionをアプリに追加
    • Timeline ProviderによってWidgetを構成
      • コンテンツを更新するタイミングをWidgetKitに知らせる
    • SwiftUIのViewを使いコンテンツを表示
    • カスタムSiriKit Intentの定義をExtensionに追加
      • ユーザーがWidgetを構成できるようになる
かむいかむい

Creating a widget extensionより抜粋

  • 様々なWidgetを提供することで、ユーザーは自分にとって最も重要な情報に集中できる
  • 作成の流れ
    • Widget Extensionテンプレートが出発点
      • 単一のWidget Extensionに複数のWidgetを含めることができる
        • ただし位置情報を求めるWidgetとそうでないWidgetとでExtensionを分けて用意すると、片方で位置情報の許諾は不要にできる(後述)
  • 構成の詳細の追加
    • 重要: WidgetがWidget Galleryに表示されるようにするためには、アプリインストール後にWidgetを含むアプリを少なくとも1回は起動する必要がある
    • @main属性の存在
      • このWidgetがWidget Extensionのエントリポイントであることを示し、ExtensionにWidgetが1つ以上あることを意味する
  • Timeline Entry
    • TImeline ProviderはTimeline Entryから構成されるTimelineを生成
      • それぞれのEntryでコンテンツを更新する日時を指定
      • またWidget Galleryに表示するためのSnapshotをTimeline Providerに求める
        • プレビュー用のSnapshotをすぐ求めるため、画像生成にサーバー問い合わせなどで時間がかかる場合はサンプルデータを用意する
    • Timeline Entry群と次のTimelineをリクエストするタイミングをWidgetKitに通知する再読み込みポリシーの2つで構成したものを最終的に返す
  • Placeholder Widget
    • 初回にWidgetを表示する際、ViewをPlaceholderとして描画
  • コンテンツの表示
    • Widget Galleryで選択したサイズごとに色スキームなどの設定を行える
      • ここで@Environment(.widgetFamily) var family: WidgetFamilyを定義した場合、systemMidiumサイズのWidgetが必須になる? -> TODO: あとで確認
    • 重要: Widgetは読み取り専用の情報を提供し、スクロール要素やスイッチなどの対話型要素はサポートしていない。描画時にそれらの要素は省略される
  • ダイナミックコンテンツの追加
    • .widgetURL修飾子を使うことでWidget押下後にDeepLinkを通し指定のアプリ画面に遷移することができる
    • サイズのうちsystemMidium, systemLargeを利用する場合はLinkコントロールを利用することもできる
      • .widgetURLと併用可
  • App Extensionで複数のWidgetの宣言
    • WidgetBundleに準拠した構造体を作り、そのbodyプロパティでWidget群をひとまとめにする構造体を宣言
    • この構造体に@main属性を与えることでExtensionが複数のWidgetをサポートしていることをWidgetKitに知らせる
かむいかむい

Keep a widget up to dateより抜粋

  • Widgetが画面上にある場合でもWidget Extensionは常時アクティブでないといけない
    • WidgetKitは個別のプロセスでユーザーに代わりViewを描画するため
    • 常にアクティブでない状態にもかかわらず、いくつかの方法でWidgetのコンテンツを最新の状態にする方法がある
  • バジェットに収まる再読み込みの計画
    • WidgetKitがシステム負荷を管理するために、各自のデバイスのWidget単位で動的に割り当てリクエストする更新頻度や回数を必要分だけに制限
    • Widgetの読み込みを1日を通じて分散し適用
      • 午前0時ちょうどにリセットされるものではない
    • ex) ユーザーが頻繁に表示するWidgetの場合
      • 1日単位のバジェットに40-70回更新(15-60分起きWidgetの再読み込みが走る間隔)
        • 多くの要因によりこの間隔は変化
        • ex) 前回の再読み込み日時, Widgetを持つアプリがアクティブか否か, Widgetの表示頻度
    • システムは数日かけてユーザー行動を学習
      • この期間中にWidgetで通常より多くの再読み込みが行われることがある
    • バジェットにカウントしないケース
      • ex) Widgetを持つアプリがフォアグラウンド, システムロケール変更, アクセシビリティの設定変更
        • システム側の変更が入った際にアプリ側でTimelineの更新は行わないこと。システム側でWidget更新を実施するため
    • Timeline Providerが再読み込みのスケジュールを主導するが、システム側が更新することもある
      • ex) ユーザーがほとんど閲覧しないホーム画面のページに置いてあるWidget, 位置情報を利用するWidgetでユーザーの位置情報に大幅な変更が見られた時
    • 将来の適切な再読み込みタイミングがわかる場合はできる限りその時刻をTImeline生成時に当てる
    • ただし最低限5分は次の更新までに間隔をあけること
  • 予測可能なイベントのTimeline生成
    • 多くのWidgetではそのコンテンツが更新の意味を持つ予測可能な時点が存在
      • ex) 1時間起きの更新が必要な天気, 休日の更新が不要な株式市場
    • カスタムのTimeline Providerを実装し、そのTimelineを使いWidgetを更新するタイミングを追跡
    • TimelineはTimelineEntryオブジェクトの配列
    • 各Entryには更新日時とViewに表示するための必要な情報を持つ
    • Timeline Entryの配列と合わせて新しいリクエストをするタイミングを指定(更新ポリシー)
    • 更新ポリシー
      • atEnd: デフォルト。配列要素の最後のTimeline Entryの日時のあとに新しいTImelineをリクエスト
      • never: アプリ側でTimelineリクエストを行うまで別のTimelineのリクエストを実施しない
      • after(_:): 指定のn時間後にリクエストを実施
    • 重要
      • 更新ポリシーでafterを利用する場合、Widgetの再読み込みでサーバー問い合わせを行う場合は計画を事前にしっかり考慮すること
        • 複数のデバイスが同時刻に再読み込みを行うとサーバ負荷が大幅に増加する可能性があるため
  • アプリ側でTimelineの更新を通知
    • WidgetCenterを使い実施
    • WidgetBundleを使い複数のWidgetをサポートしている場合、WidgetCenterを使いすべてのWidgetのTimelineを更新することができる
  • WidgetKitがライブで更新する時間ベースの情報をWidgetに表示することもできる