【Swift】Widgetの作成方法

# Widgetの準備をする
Widgetを作成したいプロジェクトをXcodeから開く。

File → New → targetを選択
![file.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788885/096b45ee-5447-d54c-2195-5fade82fd7b6.png)

Widget Extensionを選択
![addWidget.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788885/4a8956fb-6d55-8bfc-8af5-4dc98f74f3a9.png)

こんな感じで追加されていればOK！
![checkAddWidget.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788885/1d8a099b-9d4a-77f3-e6d7-75d1e48494b5.png)

主なファイルの役割は以下の通りです
<dl>
    <dt>Widget</dt>
    <dd>Widgetの見た目などを設定する</dd>
    <dt>WidgetBundle</dt>
    <dd>異なる種類のWidgetを扱える</dd>
    <dt>WidgetLiveActivity</dt>
    <dd>Live Activityを使用する時に使うファイル</dd>
</dl>


# コードの編集
1種類しかWidgetを使わないのであれば、WidgetBundleは必要ないので、全てコメントアウトします。

## Widget.swift
Widgetファイルを編集していく

### まず扱うデータを定義する。
デフォルトの状態で
```Widget.swift
struct SimpleEntry: TimelineEntry {
    let date: Date
    let emoji: String
}
```
となっている場所がある。
ここではWidgetで扱うデータの型を指定する。
ここで注意するのは、
```swift
let date: Date
```
は必ず必要だということ。
例えばStringの配列を扱いたいのであれば
```Widget.swift
struct SimpleEntry: TimelineEntry {
    let date: Date
    let data: [String]
}
```
と記述する。

### データを取得する
Widgetで扱うデータを取得するのは、一番上にあるProvider内で行います。
まず、データを取得する前に仮置きするデータをあらかじめ決めておきます。
＊すぐに表示できるように、データはここ以外の場所に置かないようにします。（データベースは使わない）
``` Widget.swift
struct Provider: TimelineProvider {
    //仮置きするデータを定義する関数
    func placeholder(in context: Context) -> SimpleEntry {
        //以下のように書いて定義する
        SimpleEntry(date: Date(), data: ["データ取得中"])
    }
}
```
このように、必ずSimpleEntry(先ほど定義したデータ型)を返すようにする。

そして、ホーム画面に追加するWidgetを選ぶ画面で表示するデータもあらかじめ定義します。
＊すぐに表示できるように、データはここ以外の場所に置かないようにします。（データベースは使わない）
``` Widget.swift
struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        ...
    }
    //仮置きするデータを定義する関数
    func func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { {
        //以下のように書いて定義する
        let entry = SimpleEntry(date: Date(), data: ["サンプルデータ"])
        complpetion(entry)
    }
}
```
completionの（）の中に返したいデータを指定します。
ここでも必ずSimpleEntryを返す

最後に本当に表示したいデータを定義します。
``` Widget.swift
struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        ...
    }
    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { {
        ...
    }
    //本当に表示したいデータを定義する
    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        //以下のように書いて定義する
        let entry = SimpleEntry(date: Date(), data: ["サンプルデータ"])
        completion(Timeline(entries: [entry], policy: .atEnd))
    }
}
```
ここで注意するのは、SimpleEntryの配列をentriesに指定することで、Timelineという型を返す。

また、UserDefaultsを使うときはちょっとした工夫が必要だが、以下に書いてある。

https://qiita.com/shuuujin/items/bf1a38bf3b1c2215b12e

またアプリでの変更をWidgetに即時反映させたい時は、UserDefaults操作の直後に
```swift
WidgetCenter.shared.reloadAllTimelines()
```
を記述する。

## 見た目を作る
Widgetは必ず、SwiftUIというフレームワークを使用して見た目を作る。
WidgetEntryViewというところに書いていく。
以下に例を示しておく。

``` Widget.swift
struct WidgetEntryView : View {
    //扱いたいデータが詰まっている変数
    var entry: Provider.Entry
    
    //この中に見た目をかく
    var body: some View {
        //ForEachは、tableViewみたいな、同じ見た目を複数作成するときに使う
        //entry.dataのところは配列型、id: \.selfはとりあえずそのまま書いておけばOK
        //dataは、entry.dataで取り出したデータのうちの、一つの要素のこと
        ForEach(entry.data, id: \.self) { data in
            //縦に並べるものをこの中にかく。横に並べる時はHStack、重ねたい時はZStack。
            VStack {
                //Textは文字を表示する。（）の中に表示したい文字列を指定する。
                //.を使って、見た目を整える。
                Text(data[0])
                    .frame(maxWidth: .infinity, alignment: .leading)
                Text(data[1])
                    .foregroundStyle(.secondary)
                    .font(.caption)
                    .frame(maxWidth: .infinity,alignment: .leading)
                    .padding(.bottom, 2)
            }
        }
    }
}
```

これでWidgetを最低限表示できるようになりました！

お疲れ様でした！