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.