Greybook
+ NewSign in

Build log: rewriting our iOS widgets, day by day

Build logBeing testedClaude Code2.1.218SwiftUI

Posted by swiftcraft · Jun 9, 2026

Our app shipped widgets in 2020 and they've been load-bearing legacy ever since: three targets, a shared framework nobody remembers agreeing to, and timeline code that predates half the WidgetKit API surface. Eight days rewriting the suite with Claude Code 2.1.218 + XcodeBuild MCP. Day-by-day, with costs, because iOS build logs are rare here and the failure modes are platform-specific.

Day 1 — Archaeology. $4.10. No code. Claude Code mapped the three widget targets, the shared WidgetCore framework, and — the actual treasure — a written inventory of which timeline providers hit the network directly (two, both illegally by modern budget rules, both the source of our "widgets go stale" reviews). Ended the day with a migration plan doc and per-target CLAUDE.md files. The plan doc misidentified one thing: it assumed our lock screen complications were WidgetKit already. They were ClockKit. More on that catastrophe later.

Day 2 — Shared timeline engine. $9.30. The core rewrite: one TimelineEngine in WidgetCore that all providers consume, app-group storage for prefetched data so providers never touch the network. Agent one-shotted maybe 70% of this from the plan doc; the 30% was Swift 6 concurrency, where it initially sprinkled @unchecked Sendable twice (my AGENTS.md forbids it; it did it anyway; second prompt citing the rule fixed both properly with an actor). Rule-following note: prohibition compliance improved dramatically when I moved that rule to the top five lines of the file. Position matters.

Day 3 — First widget migrated. $7.90. Smallest target first. The XcodeBuild MCP loop carried: build, install to simulator, screenshot the widget gallery, iterate. Watching the agent catch its own layout regression from a screenshot — text truncating in the medium family that compiled fine — sold me on screenshot-in-the-loop permanently. Cost note: screenshots plus build logs are token-heavy; this is where the money goes, and where my xcfilter script earns its keep.

Day 4 — The pbxproj incident. $6.20 plus one hour of fear. Agent added the new widget files by editing project.pbxproj directly — day 4 was a fresh session and the no-pbxproj rule was in the root CLAUDE.md, not the widget target's file it had actually read. Corrupted references, Xcode wouldn't open the project. Git saved us in five minutes; the hour of fear was auditing what else that session touched. Fixes: rule duplicated into every per-target file, and the project moved to XcodeGen that afternoon — the failure class is now extinct, which is the correct response to a near-miss. (This incident is why my AGENTS.md advice in other threads is so shouty about pbxproj.)

Day 5-6 — Remaining targets plus the ClockKit surprise. $21.40. Two more widgets migrated smoothly on the day-3 pattern — the engine consolidation meant each was mostly deletion, agent-assisted deletion being an underrated genre. Then the lock screen complications: remember day 1's misidentification? The agent had planned a trivial port; reality was a ClockKit-to-WidgetKit rewrite with no shared abstractions. Two full sessions, and the agent kept trying to preserve ClockKit concepts inside WidgetKit code — the plan doc's wrong frame stuck, surviving even a session restart because the restart read the same doc. Fixed only when I edited the plan doc itself. Write-down for the ages: agents inherit your documents' mistakes with more loyalty than your intentions.

Day 7 — Timeline budget tuning. $12.80. The subtle stuff: reload budgets, relevance scores, stale-data windows. Genuinely hard because the feedback loop is hours long — you can't screenshot your way to knowing if iOS honors your reload cadence. Agent wrote a debug harness logging timeline requests to app-group storage, which turned tomorrow's questions into today's data. Best $4 of the project.

Day 8 — QA and ship. $8.90. Full pass: all families, light/dark, three Dynamic Type sizes, stale-data behavior with airplane mode. Agent drove the simulator matrix via MCP and produced a screenshot grid; I reviewed it like a contact sheet. Two real catches (a color asset missing from one target, accessibility label on the gauge widget announcing raw numbers). Shipped in the next release train.

Totals: $84.10, eight days, three targets, and the stale-widget review complaints stopped. Codex never entered this log because Xcode-shaped work without a build loop is fiction-writing — see the SwiftUI comparison thread for the receipts. The XcodeBuild MCP loop is the whole reason this project was agent-viable at all.

Discussion · 5 comments

Strong evidence gets promoted into the record above.

junipers

'Agents inherit your documents' mistakes with more loyalty than your intentions' deserves to be the site motto. I hit the exact same thing with a stale ARCHITECTURE.md — the agent trusted my two-year-old lies over the code in front of it, repeatedly, across sessions. Docs are load-bearing for agents in a way they never were for humans, who at least had the decency to not read them.

21
hbecker

The day 5-6 ClockKit trap generalizes to a testable claim: agents are strong at migrations where old and new share a conceptual skeleton, and quietly terrible when the frames differ — they build bridges between concepts that shouldn't touch. Matches my Flutter-to-native attempts precisely. The fix being 'edit the plan doc, not the agent' is the transferable lesson.

16
swiftcrafttrusted

Yes — and note the failure needed BOTH a wrong doc and a plausible bridge. The pbxproj mistake got caught instantly because the failure was loud. The ClockKit frame survived days because every intermediate state compiled. Loud failures are a gift; it's the quiet plausible ones that need process.

13
theotv

The debug harness on day 7 is the move I want people to notice: when the feedback loop is too slow for iteration, the agent's job isn't the answer, it's building instrumentation that shortens the loop. Same pattern saves me in gamedev with frame-timing work. 'Best $4 of the project' — believable, and it'll still be earning in six months.

14
ana.duarte

Reviewing the day 8 screenshot grid 'like a contact sheet' is a genuinely good QA pattern — humans are excellent at spotting the one wrong cell in a grid of similar images, way better than at sequential inspection. I've started requesting agent QA output in grid form for exactly this reason. Also: the raw-numbers gauge label catch means your matrix included VoiceOver, which puts your agent QA ahead of most human QA I've audited.

12

Sign in to join the discussion, vote, and verify fixes.