How to Cache Widget Timelines Without Showing Stale Data

Widgets have strict refresh limits. If you fetch aggressively, you waste budget. If you cache too aggressively, users see stale data. The right approach is cache policy by data freshness class.

Step 1: Label data as hot, warm, or cold

enum FreshnessClass {
    case hot(maxAgeSec: Int)
    case warm(maxAgeSec: Int)
    case cold(maxAgeSec: Int)
}

Step 2: Build timeline entries from cache first

func snapshot(for key: String) async -> Entry {
    if let cached = cache.read(key), !cached.isExpired {
        return cached.entry
    }
    return await fetchAndStore(key)
}

Step 3: Schedule refresh based on data class

let next = Date().addingTimeInterval(TimeInterval(policy.maxAgeSec))
return Timeline(entries: [entry], policy: .after(next))

Pitfalls

  • One fixed refresh interval for all widget content.
  • Network fetch in every timeline callback.
  • No visibility into cache hit/miss rates.

Validation

  • Cache hit ratio improves without stale complaints.
  • Hot data refreshes faster than cold data by policy.
  • Widget reload budget remains within platform limits.

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.