TimelineProviderとは?ウィジェットの更新

この記事は約9分で読めます。

TimelineProviderは、SwiftUIウィジェットで時間に基づいてコンテンツを更新するためのプロトコルです。ウィジェットが定期的に表示内容を変える必要がある場合、このプロトコルを実装します。TimelineProviderは、複数のTimelineEntryを生成し、ウィジェットがどのタイミングで表示内容を更新するかを指定します。

  • iOS:14.0以上
  • XCode(当サイトの環境):15.0.1

基本的な機能

  • placeholder:ウィジェットのプレビュー用のデータを提供。※有効な使い方がはっきりとわかりませんでした。
  • getSnapshot:ウィジェットのスナップショットを取得。
  • getTimeline:ウィジェットの時間ベースのエントリを生成し、更新タイミングを制御。

getSnapshot

getSnapshot(in:) は、SwiftUI のウィジェットにおいてウィジェットの静的なプレビューを提供するために使用されます。
このメソッドは、ウィジェットがアプリ内で一瞬表示されたときや、ウィジェットギャラリーで表示される際に、その状態を取得して表示するために使われます。

getSnapshot(in:) は、通常はシンプルなデータでウィジェットを表現し、動的なデータが必要ない場合に使います。

func getSnapshot(in context: Context, completion: @escaping (WeatherWidgetEntry) -> ()) {
        let entry = WeatherWidgetEntry(date: Date(), temperature: "25°", condition: "☁️")
        completion(entry)
    }

この例では、現在の日付と固定のデータ(25°, 曇り)を含む WeatherWidgetEntry を提供します。
画像はWidgetを追加する時に表示されるスナップショットです。

getTimeline

getTimeline(in:completion:) は、SwiftUIのウィジェットでタイムラインに基づいたデータを取得し、それを描画するために使用されます。

このメソッドは、将来の状態を複数のエントリーとして提供し、ウィジェットがそのタイムラインに基づいて更新されるようにします。

メソッドの構造

getTimeline メソッドは、指定された時間範囲で複数の TimelineEntry を作成し、それらを Timeline オブジェクトで返します。

サンプルコード

下記の例は変化がわかりやすいように10秒ごとに更新するようにしています。

    func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherWidgetEntry>) -> ()) {
        var entries: [WeatherWidgetEntry] = [WeatherWidgetEntry(date: Date(), temperature: "30°", condition: "☔️")]

        // 5つのエントリーを10秒ごとに作成
        for offset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .second, value: offset * 10, to: Date())!
            let entry = WeatherWidgetEntry(date: entryDate, temperature: "\(30 + offset)°", condition: "⚡️")
            entries.append(entry)
        }
        
        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }

解説

entries

entries は、タイムラインに表示される複数のウィジェットエントリーを保持します。

entryDate

for文で5つのentryを作成しています。
entryDateはentryごとに現在の時間から10秒後、20秒後、30秒後…と日時を設定しており、この値の日時でタイムラインを表示します。

Timeline

Timeline オブジェクトは、エントリーの配列と更新ポリシーを返します。

entriesには、for文で作成したentryを5つ設定し、10秒ごとに表示が更新されるように作成しています。そして、policyに”atend”という値を設定しますが、これはリロードのタイミングを示しています。

タイムラインの最終日時、この例では現在日時+50秒後が経過したらリロードし、新しいタイムラインを要求します。

タイムラインの更新タイミング

タイムラインの更新タイミングは、Timeline オブジェクトの policy プロパティによって決まります。
このプロパティは、ウィジェットが次にいつ更新されるかを制御します。

例えば、上記のサンプルコードのように、TimelineReloadPolicy.atEnd は、最後のエントリーが表示された後に再度更新されるように指示します。

他にも、after(date:) で特定の時間に更新するよう設定できます。

let timeline = Timeline(entries: entries, policy: .after(Date().addingTimeInterval(3600)))

この場合、ウィジェットは1時間後に更新されます。

サンプルコード全文

動作確認が可能なように、ViewやTimelineEntryを含めたサンプルコードを以下に示します。

import WidgetKit
import SwiftUI

struct WeatherWidgetEntry: TimelineEntry {
    let date: Date
    let temperature: String
    let condition: String
}

// ※本ページの開設対象TimelineProvider
struct WeatherWidgetProvider: TimelineProvider {
    func placeholder(in context: Context) -> WeatherWidgetEntry {
        WeatherWidgetEntry(date: Date(), temperature: "20°", condition: "☀️")
    }

    func getSnapshot(in context: Context, completion: @escaping (WeatherWidgetEntry) -> ()) {
        let entry = WeatherWidgetEntry(date: Date(), temperature: "25°", condition: "☁️")
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherWidgetEntry>) -> ()) {
        var entries: [WeatherWidgetEntry] = [WeatherWidgetEntry(date: Date(), temperature: "30°", condition: "☔️")]

        // 5つのエントリーを10秒ごとに作成
        for offset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .second, value: offset * 10, to: Date())!
            let entry = WeatherWidgetEntry(date: entryDate, temperature: "\(30 + offset)°", condition: "⚡️")
            entries.append(entry)
        }
        
        let timeline = Timeline(entries: entries, policy: .atEnd)
        
        completion(timeline)
    }
}

struct WeatherWidgetEntryView: View {
    var entry: WeatherWidgetProvider.Entry

    var body: some View {
        Text(entry.temperature)
        Text(entry.condition)
    }
}

struct WeatherWidgetSmp: Widget {
    let kind: String = "WeatherWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(
            kind: kind,
            provider: WeatherWidgetProvider()
        ) { entry in
            WeatherWidgetEntryView(entry: entry)
                .containerBackground(.fill.tertiary, for: .widget)
        }
    }
}

#Preview(as: .systemSmall) {
    WeatherWidgetSmp()
} timeline: {
    WeatherWidgetEntry(date: .now, temperature: "22°", condition: "☀️")
    WeatherWidgetEntry(date: .now, temperature: "36°", condition: "☔️")
}

まとめ

TimelineProviderは、複数のエントリを作成してウィジェットの動的な更新を可能にします。例えば、時計や天気予報ウィジェットなど、時間に応じた情報を提供するのに適しています。

タイトルとURLをコピーしました