🍎

WidgetKit入門

2022/05/11に公開

About

本記事では,Appleが提供するiOS,iPadOS向けのWidgetKitについてざっくりまとめます.WidgetKitは基本的にSwiftUIでViewを書かなければならないので,本記事を読む前にSwiftUIチュートリアルなどでSwiftUIを学習することを推奨します.

実行環境

開発環境

Environment Version
MacBook Pro 13-inch, 2017
macOS Monterey 12.3.1
Xcode 13.3.1

実機デモ

Environment Version
iPhone 12 Pro
iOS 15.4.1
Environment Version
iPad Pro 12.9-inch (4th)
iPadOS 15.4.1

Widgetの追加

まずはじめに,ウィジェットをアプリのTargetに追加しましょう.File -> New -> Target を選択します.
Targetを選択
File -> New -> Target
iOSの項目からWidget Extensionを選択しましょう.Widgetの表示名は設定で変更できるので,Product NameはAppと名称が被らないようにすればなんでも大丈夫です.
Widget Extensionを選択
Widget Extensionを選択
最後にTargetをActivateしたら追加完了です.
TargetをActivate
Activateを選択
それでは,試しにビルド(もしくはPreview)してみましょう.おそらく現在時刻を表示するViewが表示されたと思います.WidgetKitでは主に,表示されるデータをfunc getTimelineで,表示されるViewをSwiftUIのvar body: some View {}内で記述していきます.

func placeholder

ウィジェットがロードされる前に表示されるデータです.仮データ的な感じで捉えてもらえばいいです.
https://developer.apple.com/documentation/widgetkit/timelineprovider/placeholder(in:)-6ypjs

func getSnapshot

現在時刻のデータを作成してViewとして表示させるタイムラインエントリを作成します.ユーザがウィジェットを追加して最初に目にするViewのデータはここになります.ウィジェットのサイズを選択する画面で表示されるViewもここのデータで決まるので,それを意識してコーディングしましょう.
https://developer.apple.com/documentation/widgetkit/timelineprovider/getsnapshot(in:completion:)-1wr76

func getTimeline

現在と(オプションで)将来の時刻のデータの配列を作成して,いつそのデータを使ったViewを表示したいのかについてのタイムラインエントリの生成をします.基本的にここでウィジェットの内部データを作成していきます.
https://developer.apple.com/documentation/widgetkit/timelineprovider/gettimeline(in:completion:)-9ph0p

Timelineの頻度

WidgetKitは,快適な動作のために幾つかの制限を設けています.そのひとつがTimelineによる更新頻度です.15分に1度ウィジェットを更新するのがAppleの推奨最短更新頻度です.ウィジェットは1日に更新して良い回数の予算が決まっています.これ以上短い頻度での更新はOS側から制限される場合があるので推奨しません.
タイムラインの更新頻度は,func getTimelineで以下のように記述して下さい.ちなみにタイムラインで生成するのは24時間分にしておくのがいいらしいです.

// 15分間隔での更新要求をするTimelineを生成
let currentDate = Date()
let futureDate = Calendar.current.date(byAdding: .minute, value: 15, to: currentDate)!
// 15 min * 96 times = 24 hours
for hourOffset in 0 ..< 96 {
	// ここに処理を書く
	entries.append(entry)
}
// 更新タイムラインの申請
let timeline = Timeline(entries: entries, policy: .after(futureDate))
completion(timeline)

ウィジェットの更新方法はTimeline以外にもいくつか存在しますが,本記事では省略します.また,ウィジェットの更新の予算は動的に変化するため,15分より短い頻度で行うことも可能な場合があります.詳しくは公式ドキュメントをご覧ください.

ウィジェットのサイズ(pt)を取得する

ウィジェットのサイズは端末の画面サイズによって異なります.通常のアプリであればUIScreenを使って本体の画面サイズ(pt)を取得することで実装できます.

// iPhone 12 Pro
let screenSize = UIScreen.main.bounds.size
print(screenSize) // (390x844)

https://developer.apple.com/documentation/uikit/uiscreen
しかし,この方法だとウィジェットだけでなくホーム画面全体のサイズになってしまうので,以下の方法でウィジェットのViewのみのサイズ(pt)を取得しましょう.

let displaySize: CGSize

https://developer.apple.com/documentation/widgetkit/timelineprovidercontext/displaysize
どちらの手法でも,第1引数(width)で横方向,第2引数(height)で縦方向のサイズ(pt)を指定することが可能です.数値の型はDoubleIntCGFloatとして扱えます.内部の値としてはCGFloatで保存されているため,計算などの場合は気をつけて下さい.
https://developer.apple.com/documentation/coregraphics/cgsize

ウィジェットのサイズ別のViewを設定する

WidgetKitには,iOSで3種類(小・中・大),iPadOSで4種類(小・中・大・極大)のサイズが用意されています.サイズによって違うViewを表示させることでウィジェットが一層使いやすく,スタイリッシュになるので是非実装してみて下さい.
ウィジェットのサイズは環境変数で取得できます.

@Environment(\.widgetFamily) var family: WidgetFamily

https://developer.apple.com/documentation/widgetkit/widgetfamily
switch文を用いて,.systemSmall.systemMedium.systemLarge.systemExtraLargeで分岐させることでそれぞれ別のViewを表示できます.戻り値がViewな関数をそれぞれ呼び出すのがおすすめです.

あとがき

最近ウィジェットのあるアプリの開発をしたのですが,WidgetKitに関する記事を探すのに苦労したので,個人的に必要になった知識をApple公式ドキュメントに沿ってまとめました.記事にまとめるのは初めてなので,拙い部分が多々あるかもしれません.
placeholdergetSnapshotgetTimelineの辺りは本記事を執筆して理解が深まったので,アウトプットって思ってた以上に大切ですね.ちなみに検索する時は”Widget”ではなく”WidgetKit”で検索した方が,調べたかった内容に辿り着きやすくなると思います.

本記事が誰かのお役に立てば幸いです.役に立ったら記事のお気に入り登録とFollowしてくれると嬉しいです.

Discussion