[iOS 14] Widgetの技術的要点をざっくり

記事の内容はこちらを参照してください。
https://mishimay.github.io/03c23f030144f863fe30/
<!--


## Widgetはミニアプリではない
- Widget上で操作することは想定されておらず、スクロールやインタラクティブなUI (e.g. Switch) や動画などはサポートされない
- ただし、Widgetをタップするとディープリンクでアプリ内の適切な画面を開ける
  - Smallサイズは1つのリンクしか持てない
  - MediumとLargeサイズは複数のリンクを持つことができ、タップした領域によって別のディープリンクを開くことができる  
  <img width="600" alt="1つのWidgetに複数のリンク" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23679/48ba8e8e-ea97-96af-fd21-2561f924e146.png">  
  ([Meet WidgetKit](https://developer.apple.com/videos/play/wwdc2020/10028/) より)

## 画面は事前に描画される
- 「Widgetを表示するタイミングでアプリを起動してロードして...」という読み込み時間がないのでWidgetの変更がすぐに反映される
  - ただし、初回表示時や設定変更時 (e.g. ダイナミックタイプの変更) には画面描画まで時間が掛かるのでPlaceholderが表示される
- SwiftUIの宣言的にViewを定義できる性質を活かした仕組み
- Timelineという仕組みによって画面更新のスケジューリングを行う

## Widgetのバリエーション
- 1つのアプリは複数種類のWidgetを持てる
  - 1つのアプリの中で複数の WidgetKit extension を持つこともできるが、1つの WidgetKit extension に複数Widgetをまとめることが推奨されている
    - `WidgetBundle` を使うことで1つの WidgetKit extension に複数のWidgetをまとめることができる
- 1つのWidgetは複数のサイズを持てる
  - `WidgetFamily` に定義されている以下3種類がある
      - systemSmall
      - systemMedium
      - systemLarge
  - どれか1つのサイズだけサポートしても良い
- 同じ種類・サイズのWidgetをホーム画面に複数個置ける
  - Widgetの設定を変えることで別の見た目にすることが可能

## Smart Stack
- 複数のWidgetを1つの枠にまとめられる機能  
<img width="400" alt="Smart Stack" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23679/2bbea351-79e5-5106-3eb4-cf18315df1ab.gif">  
([Design great widgets](https://developer.apple.com/videos/play/wwdc2020/10103/) より)
- 上下スワイプでWidget間を移動できる
- システムがその時々で一番最適なWidgetを自動で判断して表示する
  - 以下の情報をアプリから提供するとより精度が上がる
      - Shortcuts donation
          - アプリ内のユーザー操作をSiriに伝える
      - 関連度 (Relevance)
          - `TimelineEntry` に対して `score` と　`duration` を付与する
          - `score` の最大値と最小値は自由に決めて良い


## パーソナライズ (Widget Configuration)
- ホーム画面を編集モードにしてWidgetをタップするとWidgetの設定ができる  
<img width="400" alt="Smart Stack" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23679/5236ebf7-970a-7323-1c4e-2f5a6e9ed9f5.gif">  
([Design great widgets](https://developer.apple.com/videos/play/wwdc2020/10103/) より)
- Intents (i.e. `SiriKit` の `INIntent`) を用いることで可能となる
  - SiriやShortcutsとの連携でも使われている技術
  - Intentsの情報から自動で設定画面のUIが作られる
  - ネットワークを通じて設定項目を取得するなどして動的に設定項目を変えることもできる

## 各キーワードについてもう少し詳しく

### Timeline

  - いつ、どのようなViewを表示するかをあらかじめ計画する
  - Timelineの更新タイミング
    - システムによる更新
        - `ReloadPolicy` からシステムが更新に最適なタイミングを判断する
        - システムによってよく見られているWidgetだと判斷された場合はシステムがより頻繁に更新する
            - つまり `ReloadPolicy` 通りに更新されるわけではない？
    - アプリ起因による更新
        - `WidgetCenter` を通じて任意のタイミングで
            - アプリ本体からでも WidgetKit extension からでも
        - `URLSession` の background session が完了したタイミング
            - データをインターネットから取得してきた後など
            - 負荷を上げないために頻繁に更新しすぎないよう注意
  - 例外として、 `Text` viewのパラメーターにDate型を渡して日時を表示する場合は、意図的に更新しなくても随時最新の日時に更新される
    - 詳しくは [Keeping a Widget Up To Date](https://developer.apple.com/documentation/widgetkit/keeping-a-widget-up-to-date) の "Display Dynamic Dates" の項を参照のこと

### Snapshot

  - 現在の日時や状態などの情報が含まれる `TimelineEntry` を元にしてそれに対応する画面を返す
  - パラメーターで渡される `context.isPreview` がtrueのときは Widget Gallery で使用される
    - Widget Gallery とは、ホーム画面にWidgetを追加する際に見本のWidgetが表示される画面  
<img width="225" alt="Widget Gallery" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23679/4e066072-1ef1-7d20-d656-e22763d09214.png">  
  ([Meet WidgetKit](https://developer.apple.com/videos/play/wwdc2020/10028/) より)
    - 素早くユーザーに見本を提示する必要があるため、サーバーからデータを取得するWidgetの場合は代わりにサンプルデータを使う

### Placeholder

  - 初回表示時や設定変更時 (e.g. ダイナミックタイプの変更) に、まだTimelineが作成されていないときに表示される
  - WidgetのViewに `.isPlaceholder(true)` を追加するだけで簡単に作成できる  
  <blockquote class="twitter-tweet"><p lang="en" dir="ltr">SwiftUI gives you .isPlaceholder(true) in WidgetKit, which makes it incredibly simple to generate a placeholder Widget view ✨ <a href="https://t.co/40Fw8TzfWE">pic.twitter.com/40Fw8TzfWE</a></p>&mdash; Jordan Singer (@jsngr) <a href="https://twitter.com/jsngr/status/1276180871930929152?ref_src=twsrc%5Etfw">June 25, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## 参考資料
- https://developer.apple.com/videos/play/wwdc2020/10028/
- https://developer.apple.com/videos/play/wwdc2020/10103/
- https://developer.apple.com/videos/play/wwdc2020/10034/
- https://developer.apple.com/videos/play/wwdc2020/10035/
- https://developer.apple.com/videos/play/wwdc2020/10036/
- https://developer.apple.com/documentation/widgetkit
- https://developer.apple.com/documentation/widgetkit/creating-a-widget-extension
- https://developer.apple.com/documentation/widgetkit/making-a-configurable-widget
- https://developer.apple.com/documentation/widgetkit/keeping-a-widget-up-to-date



-->