［SwiftUI / WidgetKit］アプリのボタンをタップしたら、ウィジェットを更新する

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199441/d44b22c7-fdce-013f-5485-95a69e8f0e0f.png" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199441/9573aa3c-cd09-c461-2681-32a25fb61595.png" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199441/b505b7c9-59c4-4a17-1593-efa7ec7c01a2.png" width="200">

## 記事の内容
- アプリ内のボタンをタップしたら、ウィジェットを更新する

## この記事で記載しないこと
- App Group の設定
- UserDefaults の使用方法

## 開発環境
|項目|内容|
|---|---|
|PC| MacBook Air （2020 M1）|
|言語|Swift|
|フレームワーク|SwiftUI|
|IDE|Xcode|
|コード管理|GitHub|

## 前提条件
- App Group は、`group.sample.Emojis` で設定している

## コード
```swift: ContentView.swift
import SwiftUI
import WidgetKit

struct ContentView: View {
    
    let emojis = [
        Emoji(icon: "😃", name: "大きい目の笑顔", description: "Grinning face with big eyes"),
        Emoji(icon: "🏂", name: "スノーボーダー", description: "Snowboarder"),
        Emoji(icon: "🦄", name: "ユニコーン", description: "Unicorn")
    ]
    
    var body: some View {
        VStack(alignment: .leading,spacing: 30){
            ForEach(emojis){ emoji in
                EmojiView(emoji: emoji)
                    .onTapGesture {
                        save(emoji)
                        WidgetCenter.shared.reloadAllTimelines()
                    }
            }
        }
    }
    
    private func save(_ emoji: Emoji){
        let userDefaults = UserDefaults(suiteName: "group.sample.Emojis")!
        guard let emojiData = try? JSONEncoder().encode(emoji) else { return }
        userDefaults.set(emojiData, forKey: "Emoji")
        print("saved Emoji: \(emoji)")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

```swift:EmojiView.swift
import SwiftUI

struct EmojiView: View {
    let emoji: Emoji
    var body: some View {
        Text(emoji.icon)
            .font(.largeTitle)
            .padding()
            .background(Color.purple)
            .clipShape(Circle())
    }
}

struct EmojiView_Previews: PreviewProvider {
    static var previews: some View {
        EmojiView(emoji: Emoji(icon: "🦄", name: "ユニコーン", description: "Unicorn"))
    }
}
```

```swift:EmojisWidget.swift
import WidgetKit
import SwiftUI

struct Provider: TimelineProvider {
    
    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
        let entry = SimpleEntry(date: Date(), emoji: Emoji(icon: "💛", name: "N/A", description: "N/A"))
        completion(entry)
    }
    
    func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
        let userDefaults = UserDefaults(suiteName: "group.sample.Emojis")!
        let date = Date()
        let emojiData = userDefaults.object(forKey: "Emoji") ?? Data()
        guard let emoji = try? JSONDecoder().decode(Emoji.self,from: emojiData as! Data) else {
            let entry = SimpleEntry(date: date, emoji: Emoji(icon: "💛", name: "N/A", description: "N/A"))
            let timeline = Timeline(entries: [entry], policy: .never)
            completion(timeline)
            return
        }
        let entry = SimpleEntry(date: date, emoji: emoji)
        let timeline = Timeline(entries: [entry], policy: .never)
        completion(timeline)
    }
    
    func placeholder(in context: Context) -> SimpleEntry {
        return SimpleEntry(date: Date(), emoji: Emoji(icon: "💛", name: "N/A", description: "N/A"))
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let emoji: Emoji
}

struct EmojisWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        VStack {
            EmojiView(emoji: entry.emoji)
            Text(entry.emoji.description)
                .font(.subheadline)
                .foregroundColor(.gray)
        }
    }
}

@main
struct EmojisWidget: Widget {
    let kind: String = "EmojisWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: "Emojis_Widget", provider: Provider()) { entry in
            EmojisWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("Emojis Widget")
        .description("This is an example widget.")
    }
}

struct EmojisWidget_Previews: PreviewProvider {
    static var previews: some View {
        EmojisWidgetEntryView(entry: SimpleEntry(date: Date(), emoji: Emoji(icon: "💛", name: "N/A", description: "N/A")))
            .previewContext(WidgetPreviewContext(family: .systemSmall))
    }
}
```

## GitHub
https://github.com/tanakadaichi1989/Emojis

## 参考資料
https://developer.apple.com/jp/documentation/widgetkit/