0Pricing
Swift Academy · Lesson

Timeline Providers and Snapshots

Supply widget content over time.

Timeline Providers and Snapshots is a free Swift Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

The Provider's Job

A widget never updates itself continuously. Instead, a TimelineProvider hands WidgetKit a schedule of pre-rendered entries, and the system displays each at the right time. The provider answers three questions: placeholder, snapshot, and timeline.

import WidgetKit
// TimelineProvider supplies:
//   placeholder(in:) -> instant skeleton
//   getSnapshot(in:)  -> one entry for previews
//   getTimeline(in:)  -> future entries + refresh policy

Conforming to TimelineProvider

A provider conforms to TimelineProvider with an associated Entry type. You implement the three methods; each gives you a Context describing the family and whether it is a preview.

import WidgetKit
struct WeatherProvider: TimelineProvider {
    typealias Entry = WeatherEntry
    func placeholder(in context: Context) -> WeatherEntry {
        WeatherEntry(date: Date(),
            temperature: 20, condition: "Sunny")
    }
    // getSnapshot and getTimeline follow
}

The Placeholder

placeholder(in:) must return instantly with representative dummy data. The system shows it as a redacted skeleton while the real widget loads and in the widget gallery. Never do network or disk work here.

import WidgetKit
func placeholder(in context: Context) -> WeatherEntry {
    // Synchronous, fake data, no I/O
    WeatherEntry(date: Date(),
        temperature: 0, condition: "--")
}

The Snapshot

getSnapshot provides a single entry for transient situations like the widget gallery preview. It should return quickly. When context.isPreview is true, use sample data instead of a slow fetch so the gallery feels instant.

import WidgetKit
func getSnapshot(
    in context: Context,
    completion: @escaping (WeatherEntry) -> Void) {
    if context.isPreview {
        completion(WeatherEntry(date: Date(),
            temperature: 22, condition: "Clear"))
    } else {
        completion(currentEntry())
    }
}

The Timeline

getTimeline is the heart: you build an array of future entries and bundle them with a refresh policy in a Timeline. The system renders each entry at its date, then asks for a new timeline per the policy.

import WidgetKit
func getTimeline(
    in context: Context,
    completion: @escaping (Timeline<WeatherEntry>) -> Void) {
    let entries = buildEntries()
    let timeline = Timeline(
        entries: entries, policy: .atEnd)
    completion(timeline)
}

Refresh Policies

The reload policy controls when WidgetKit requests the next timeline: .atEnd after the last entry's date, .after(date) at a specific time, or .never until you reload manually. The system budgets these, so do not expect second-by-second updates.

import WidgetKit
// .atEnd            -> reload after final entry
// .after(someDate)  -> reload at a chosen time
// .never            -> only on manual reloadTimelines
let policy = TimelineReloadPolicy.atEnd
_ = policy

Building Future Entries

A common pattern is to pre-compute the next several hours so the widget updates without waking your code each time. Generate entries at intervals from now into the future, each holding the data for that moment.

import WidgetKit
import Foundation
func hourlyEntries() -> [WeatherEntry] {
    var entries: [WeatherEntry] = []
    let now = Date()
    for hour in 0..<6 {
        let date = Calendar.current.date(
            byAdding: .hour, value: hour, to: now)!
        entries.append(WeatherEntry(date: date,
            temperature: 18 + hour, condition: "Sunny"))
    }
    return entries
}

Async Data in the Timeline

If you must fetch from the network, do it before calling completion. Wrap async work in a Task and only complete once data arrives. Keep it fast — providers have a tight time budget.

import WidgetKit
func getTimeline(
    in context: Context,
    completion: @escaping (Timeline<WeatherEntry>) -> Void) {
    Task {
        let entry = await fetchForecast()
        let timeline = Timeline(
            entries: [entry], policy: .after(
                Date().addingTimeInterval(3600)))
        completion(timeline)
    }
}
func fetchForecast() async -> WeatherEntry {
    WeatherEntry(date: Date(),
        temperature: 21, condition: "Cloudy")
}

Relevance and Budget

WidgetKit limits how often it refreshes per day to protect battery. You cannot force frequent updates. Provide several entries per timeline and pick a reasonable reload time; use reloadTimelines from the app only on meaningful data changes.

import WidgetKit
// The system, not you, decides exact refresh timing.
// Strategy: pre-bake multiple entries + a sensible policy
// + app-driven reloads on real changes.
let budget = "refreshes are budgeted by the OS"
_ = budget

Provider for Configurable Widgets

For an AppIntentConfiguration widget, use AppIntentTimelineProvider instead. Its methods receive the user's configured intent, so you can fetch data for the chosen option (the selected city, account, etc.).

import WidgetKit
// AppIntentTimelineProvider adds the configuration:
//   func timeline(for configuration: MyIntent,
//                 in context: Context)
//       async -> Timeline<Entry>
let configurable = "intent-aware provider"
_ = configurable

Putting the Provider Together

A complete provider gives an instant placeholder, a quick preview-aware snapshot, and a timeline of pre-baked entries with an appropriate reload policy — so the widget stays current within the system's budget.

import WidgetKit
// 1. placeholder -> instant dummy
// 2. getSnapshot -> sample when isPreview, else current
// 3. getTimeline -> [entries] + .atEnd or .after
let summary = "three methods, one current widget"
_ = summary

Quick Check

Recall the constraint on the placeholder method.

Recap

You learned timeline providers:

  • placeholder returns instant dummy data; getSnapshot returns one entry (sample when isPreview); getTimeline returns future entries plus a policy.
  • Reload policies are .atEnd, .after(date), or .never; the OS budgets actual refreshes.
  • Pre-bake multiple entries; do async fetches before completing.
  • Configurable widgets use AppIntentTimelineProvider.

Frequently asked questions

Is the “Timeline Providers and Snapshots” lesson free?

Yes — the full text of “Timeline Providers and Snapshots” is free to read here on the web, and the Swift Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Swift Academy course, upgrade to CoddyKit PRO.

What will I learn in “Timeline Providers and Snapshots”?

Supply widget content over time. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Swift Academy?

No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Timeline Providers and Snapshots” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Swift Academy lesson?

Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Building a WidgetKit Widget
  2. Timeline Providers and Snapshots
  3. App Extensions Overview
  4. App Intents and Shortcuts
← Back to Swift Academy