ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-06-23Advanced

My Daily Wallpaper Widget Stopped Updating — Measuring WidgetKit's Reload Budget and Rebuilding the Design

A widget that was supposed to rotate the wallpaper every day froze after a few days. Here is how I measured WidgetKit's timeline reload budget and extension memory limit, then rebuilt the design around a single daily timeline.

WidgetKit3iOS23Antigravity261wallpaper appperformance12

Premium Article

I added a feature to my wallpaper app that rotates the home-screen widget image every day. In the simulator it behaved beautifully. On a real device left running for a few days, the widget froze one morning — the same image, hour after hour, never changing. Opening the app fixed it. Closing it and waiting brought the freeze back.

My first guess was a caching bug or a bad date calculation. But the cause was not a coding mistake. As an indie developer running this widget for real, I was hitting WidgetKit's "reload budget" — a hard limit that appears nowhere on screen — every single day. It is a place you reach the moment you take widgets seriously, so I want to leave behind the numbers I measured and the way I rebuilt the design.

Widgets Are Not "Alive"

There is one misconception worth clearing up first. A widget is not a small app running continuously. What you see is a still frame the OS draws from a prepared "timeline."

A timeline is an array of TimelineEntry values, each one a declaration: "at this time, show this content." The system calls getTimeline(in:completion:), receives the array, and swaps the view when each entry's date arrives. Your code does not run on every swap. The moment you hand back the array, everything downstream belongs to the OS.

struct WallpaperProvider: TimelineProvider {
    func placeholder(in context: Context) -> WallpaperEntry {
        WallpaperEntry(date: Date(), assetID: "placeholder")
    }
 
    func getSnapshot(in context: Context, completion: @escaping (WallpaperEntry) -> Void) {
        completion(WallpaperEntry(date: Date(), assetID: currentAssetID()))
    }
 
    func getTimeline(in context: Context, completion: @escaping (Timeline<WallpaperEntry>) -> Void) {
        // Build the whole array here. The number of calls is what spends the budget.
        let entries = buildDailyEntries(from: Date())
        completion(Timeline(entries: entries, policy: .atEnd))
    }
}

So if you translate "change the wallpaper daily" into "call getTimeline daily to fetch a new image," you have already taken a wrong turn. That is exactly the turn I took first.

The Invisible Limit Called Reload Budget

There are three main triggers that call getTimeline again: the timeline's reloadPolicy expiring, the app calling WidgetCenter.shared.reloadTimelines(ofKind:), and the system deciding on its own.

Background reloads among these draw from a per-widget daily budget. Apple does not publish the exact figure, but for frequently visible widgets a range of roughly 40–70 reloads per day shows up repeatedly, both in the developer community and in my own measurements. The budget replenishes gradually over the day.

My first implementation called reloadTimelines on every foreground and on a one-hour timer. The timeline itself was a short "one entry now plus .atEnd an hour later." The result was around 120 background reloads requested per day. I burned through the budget by late morning, and the OS quietly deferred reloads for the rest of the day. That was the real cause of "freezes one morning."

There is no API that tells you whether the budget is exhausted. No error fires. The image simply goes stale. The nastiest part of this trap is that the budget is generous on the simulator and on a device during active development, so it only surfaces after the app has been left alone for a while.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Switching from per-request reloads to a 24-hour timeline cut background reloads from roughly 120/day to 3/day in my own measurements
Fixing the ~30MB widget extension memory ceiling that killed full-resolution wallpapers, dropping decoded memory from ~38MB to 0.4MB with ImageIO
Instrumentation that indirectly surfaces the OS reload budget by counting getTimeline calls in an App Group container
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

App Dev2026-05-26
Unifying In-App Review Prompts Across 5 Apps with Antigravity Editor — A Few Days of Notes
Notes from a few days spent unifying SKStoreReviewController trigger conditions across five iOS wallpaper apps I run as an indie developer, using Antigravity Editor's multi-file editing to bring the logic onto one shared coordinator.
App Dev2026-05-25
One Month Splitting Antigravity's Inline Edit and Agent Mode Across Four Wallpaper Apps
A month of notes from running Antigravity's Inline Edit and Agent Mode across four production wallpaper apps — with real counts, the decision rule I wrote into AGENTS.md, a one-pass dSYM fix, and how credit cost factors in.
App Dev2026-05-16
Surviving New iPhone Resolution Support with Antigravity — 29 Changes in DefineManager.h, One Honest Recap
A real-world account of updating Beautiful HD Wallpapers and three other iOS apps for iPhone Air (420×912) and iPhone 17 Pro (402×874). What Antigravity caught, what it missed, and the two-step prompt pattern that made the 29-edit process manageable.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →