Skip to content

Mastering TimelineView (SwiftUI)

A SwiftUI guide for coding agents. Also covers timelineview animation schedule, update view on a timer, periodic view refresh, custom timelineschedule, cadence apple watch, time based ui updates.

TimelineView re-evaluates its body on a schedule instead of on data changes. Reach for it when the UI depends on time itself: clocks, timers, and frame-driven animation.

The Context Hands You the Date

The ViewBuilder closure receives a context whose date is the timestamp of the current update. Derive everything you draw from that date.

TimelineView(.animation) { context in
  let value = secondsValue(for: context.date)

  Circle()
    .trim(from: 0, to: value)
    .stroke()
}

private func secondsValue(for date: Date) -> Double {
  let seconds = Calendar.current.component(.second, from: date)
  return Double(seconds) / 60
}

The .animation schedule updates at the platform's animation rate, so the body runs every frame.

Cadence Changes Under You

context.cadence is the current update rate: .live, .seconds, or .minutes. The system can lower it during the view's life, for example when an Apple Watch user drops the wrist. Cadence is Comparable, so branch on it and render a cheaper version at slow cadences.

TimelineView(.animation) { context in
  let value = context.cadence <= .live
    ? nanosValue(for: context.date)
    : secondsValue(for: context.date)

  Circle().trim(from: 0, to: value).stroke()
}

Built-In Schedules

Besides .animation, SwiftUI ships .everyMinute and .periodic, which fires from a start date at a fixed interval.

TimelineView(.periodic(from: .now, by: 5)) { context in
  // body runs every 5 seconds
}

Custom Schedules

Conform to TimelineSchedule and return the update dates from entries(from:mode:). A convenience extension gives it dot syntax at the call site.

final class DailySchedule: TimelineSchedule {
  typealias Entries = [Date]

  func entries(from startDate: Date, mode: Mode) -> Entries {
    (1...30).map { startDate.addingTimeInterval(Double($0 * 24 * 3600)) }
  }
}

extension TimelineSchedule where Self == DailySchedule {
  static var daily: Self { .init() }
}

TimelineView(.daily) { context in /* ... */ }

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-swiftui-guide({ topic: "swiftui-mastering-timelineview" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems