[Swift]ウィジェット(WidgetKit)をデフォルトのコードを使って表示させてみた

この記事では、iOS14から導入されたWidgetKitをデフォルトのコードを使ってウィジェットを表示させてみたので共有します。
2021.06.16

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

WidgetKitとは?

  • アプリを開くことなく、ホーム画面上にアプリの情報を表示できる機能のこと。
  • iOS14からホーム画面にウィジェットが表示できるようになりました。
  • iOS13以前はToday Extentionという機能を使って通知センターに表示することができていました。
  • ウィジェットはSwiftUIを使って作成します。
  • サイズは3種類あります(small, medium, large)

WidgetKitの目的

  • Glanceable(ユーザーが欲しい情報がすぐに確認できる)
  • Relevant(ユーザーの時間場所場合に適した表示)
  • Personalaized (個人に合わせたカスタマイズ性能)

ウィジェットを表示させる方法

ウィジェット表示させたいアプリを作成する

File→New→Project→App からアプリを作成します。

WidgetKitの追加方法

File →New →Target →Widget Extension
今回はWidgetTestという名前にしました。

Include Configuration Intentとは?
Widget Extensionを追加する際にInclude Configuration Intentという項目を有効にするか選択できます。

Configuration Intentを有効にすることで、ウィジェットで表示する内容をユーザーが設定することができるようになります。(IntentConfigurationといいます)

有効にしなかった場合は、ユーザーがウィジェットで表示する内容を変更することができないタイプのウィジェットになります。(StaticConfigurationといいます)

この内容は後ほど、コード上で変更することも可能です。

これでFinishを押して作成するとWidgetTest.swiftというファイルが生成されます。

このWidetTest.swiftを使ってウィジェットの表示に必要な処理を記載していきます。

WidetTest.swiftには既にコードが書かれていると思うので実行していきます。

この状態で実行すると、時間を表示するウィジェットが表示されると思います(デフォルトのウィジェットのコードがそうなっています)

WidetTest.swiftのコードの内容

デフォルトで記載されているコードでは、1時間ごとに更新されるtimelineを用いて現在時刻を1時間ごとに表示させています。

import WidgetKit
import SwiftUI
import Intents

// ウィジェットに表示する内容とタイミングを設定する
// StaticConfigurationの場合はTimelineProviderになっています
struct Provider: IntentTimelineProvider {
    // ウィジェットがロードされる前に表示されるデータ
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), configuration: ConfigurationIntent())
    }
    // プレビュー/ギャラリーに表示されるビュー、ユーザーがウィジェットを追加した際に最初に表示されるビュー
    func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), configuration: configuration)
        completion(entry)
    }
    // 複数のビューと日付の組み合わせで、いつどのビューを表示したいかを設定する
    func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        var entries: [SimpleEntry] = []

        // Generate a timeline consisting of five entries an hour apart, starting from the current date.
        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(date: entryDate, configuration: configuration)
            entries.append(entry)
        }
        // タイムラインポリシー(atEndの場合は、最後のEntryが表示された際にreloadをします)
        // 他にもnever, afterがあります。
        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

// ウィジェットに使用されるデータ
// TimeLineEntryではdateが必須になっています
struct SimpleEntry: TimelineEntry {
    let date: Date
    let configuration: ConfigurationIntent
}

// ウィジェットで表示させる内容を設定します
struct WidgetTestEntryView : View {
    var entry: Provider.Entry
    
    // ウィジェットのビューで表示させる内容(デフォルトは時間)
    var body: some View {
        Text(entry.date, style: .time)
    }
}

// ウィジェットをユーザーが設定する際に表示する内容
@main
struct WidgetTest: Widget {
    // WidgetKitは複数のウィジェットを構築することができるので、ウィジェットごとの識別子を設定します
    let kind: String = "WidgetTest"

    var body: some WidgetConfiguration {
        IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
            WidgetTestEntryView(entry: entry)
        }
        // ユーザーがwidget設定時に表示されるタイトル
        .configurationDisplayName("My Widget")
        // ユーザーがwidget設定時に表示される説明文
        .description("This is an example widget.")
    }
}

struct WidgetTest_Previews: PreviewProvider {
    static var previews: some View {
        WidgetTestEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
            .previewContext(WidgetPreviewContext(family: .systemSmall))
    }
}

終わりに

ウィジェットは他にも機能があるので次回以降の記事で紹介していければと思います。