◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude Code
⟐ Claude Code/2026-05-26Intermediate

Four Weeks With Claude Code: Driving Xcode Warnings to Zero in an Indie iOS Codebase

An indie iOS developer behind 50M+ downloads pairs with Claude Code for four weeks to clear hundreds of accumulated Xcode warnings. Notes on weekly scope, what to delegate, and the boundary where human judgment still wins.

Claude Code264Xcode4iOS26Refactoring3Indie Development11

I'm Masaki Hirokawa, an artist and indie developer running a small collection of wallpaper and wellbeing iOS apps. Once cumulative downloads crossed 50 million, the warnings piling up in my Xcode build logs started to bother me in a quiet but persistent way. None of them were serious enough to crash the apps, but watching 300+ warnings stream past during every build felt like the kind of misalignment a craftsman would notice before anyone else.

Both of my grandfathers were temple carpenters in Japan, and one of the things I absorbed from them is that something built well shouldn't drift over decades. Compiler warnings carry that same signal for me: small misalignments that hint at where the joints have shifted. So this spring I gave myself a four-week sprint with Claude Code as a partner, aimed at getting Xcode warnings to zero. This article is the field report.

Setting the Scope Before Touching Any Code

The framing I committed to up front was not "achieve zero warnings" but "switch to a workflow that can keep warnings at zero." No new features during the sprint, no major library upgrades, no chasing of stretch refactors. The only objective was reducing the addressable warnings that showed up in the build log.

A tight scope made the context I handed to Claude Code naturally narrow. I limited the work to three production apps, captured the xcodebuild output without -quiet, and saved it as a plain text log inside the Claude Code working tree. In the first session I asked Claude to read the log and group the warnings by intent, then sort each group by the cost of fixing it.

The result was a four-bucket map: Deprecation warnings, Unused symbol warnings, places where type inference had gone fuzzy, and @available bounds that had aged out. That single step gave me more clarity in thirty minutes than I'd had by squinting at the log alone for months.

Week-by-Week: Start With the Mechanical Wins

Week 1 was nothing but Unused warnings and stale import statements. I asked Claude Code to "propose deletions for Unused Variable warnings only, without changing observable behavior," and I accepted the diffs one feature branch at a time. The rule I learned to follow: never try to clear multiple warning categories in a single commit. One category, one region, one PR. If a test failed later, rollbacks stayed cheap.

Week 2 was the Deprecation backlog. I had more leftover UIApplication.shared.statusBarOrientation calls and pre-UIScene lifecycle paths than I'd realized — APIs deprecated as far back as iOS 13 that quietly survived. Claude Code drafted the replacement strategy per API, and I personally verified iPad orientation behavior by hand because that's something a static analyzer can't feel.

Week 3 targeted fuzzy type inference and missing @MainActor annotations. This is the area where Claude Code can over-correct if you let it. I switched modes: have it propose the smallest possible change that compiles cleanly, accept only that. Treat it as scaffolding for the eventual Swift 6 strict concurrency turn-on, not the turn-on itself.

Week 4 closed out remaining @available ranges and the warnings I couldn't fix because they came from third-party SDKs. For that last category I asked Claude Code to "document every unfixable warning with the reason it must remain." The output became _docs/known-warnings.md, which now acts as the basis for any build-setting suppressions I decide to add.

What I Delegated, What I Kept

By the end of the sprint, a clean line had emerged between work I happily handed off and work I refused to. Claude Code handled the local, diff-reviewable changes well: deleting unused variables, swapping API signatures, normalizing @available ranges. These are the chores I personally don't enjoy doing, and the speed and accuracy I got back were genuinely a win.

What I kept in human hands was anything that touches felt experience — widget background rendering, scroll inertia, haptic intensity, image-loading timing. For apps that long-time users have a muscle memory for, a refactor that's logically correct can still erode the "usual feel." This is something I've learned the hard way across twelve years of indie iOS development. Zero warnings is never worth a regression that loyal users perceive but can't articulate.

The other piece I kept was the decision to intentionally retain certain warnings: ones from third-party SDKs, ones from compatibility shims I want to keep, ones from A/B-test code paths running in parallel on purpose. Claude Code wants to remove these, and I instead annotate them with a // MARK: comment that records the reason. I avoid // swiftlint:disable precisely because future-me needs the rationale, not the silence.

Friction and How I Dialed It Down

The first source of friction was Claude Code's tendency to fix too much per turn. The cure was instruction granularity. Every prompt I sent included three constraints: target a single file, target a single warning category, and keep the commit under roughly 200 lines of diff. With those guardrails, the proposals stayed reviewable.

The second was making Claude Code self-verify the build. Running xcodebuild through Claude Code produced logs too long to digest in one round. I switched to xcodebuild ... | tee build.log and told Claude Code to "read only the last 500 lines of build.log" when checking the result. Latency dropped, and review pacing improved noticeably.

The third was my own review fatigue. Claude Code was fast enough that I became the bottleneck. Mid-sprint I set a daily ceiling on how many diffs I would merge, on the theory that approving fixes faster than I could read them was the same as creating new debt with a different name.

Numbers From the Sprint

Starting warning count across the three apps: 312. Final count after four weeks: 6. The remaining six are all third-party SDK warnings logged with reasons in known-warnings.md. Total commits: about 90, of which roughly 75 were Claude-drafted diffs that I reviewed and accepted.

The biggest unexpected payoff was psychological: the resistance I used to feel when opening the build log was gone. With 300 warnings on the badge, I had unconsciously closed the log before reading it. That habit was quietly degrading the quality of every other debugging decision I made — AdMob latency, Firebase init ordering, anything that lived in build output became easier to triage once the noise was gone.

Zero new warnings were introduced during the sprint. Writing "do not introduce new warnings" into Claude Code's standing constraints turned out to be surprisingly effective. For an indie developer with a long-running codebase, the lesson I keep coming back to is that "achieve zero" matters less than "switch to a workflow that holds zero."

What's Next

The next four-week block will gradually flip on Swift 6 strict concurrency and work through Sendable violations with Claude Code. Turning the flag on across the whole codebase at once would resurrect hundreds of warnings, so I'm planning to enable it target-by-target with @preconcurrency boundaries drawn deliberately.

In parallel I want to wire up an automation where release-time xcodebuild logs flow into Claude Code, get summarized, and update _docs/known-warnings.md without my input. If I can codify "holds zero" into the build pipeline itself, I free up time for the parts of indie development I actually want to spend hours on. The self-taught programming I started in 1997 was once the goal; today it works best when it gives me back time for everything else. I hope these notes are useful to others maintaining long-lived codebases.

Share

Thank You for Reading

Claude 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 →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⟐ Claude Code2026-09-04
The One File I Keep Out of Claude Code's Reach: project.pbxproj
With Xcode project files, the breakage that still opens costs far more than the breakage that refuses to open. Here is what I measured before moving every edit behind a script, and where the line sits today.
⟐ Claude Code2026-10-12
After Moving to SPM, My App Died the Moment It Launched — Reading One dyld Line with Claude Code
Right after moving from CocoaPods to Swift Package Manager, the build passed but the app died on launch. Here is how I traced one dyld line to its cause, the fix, and how to verify it.
⟐ Claude Code2026-09-10
One plugin.json turns a shelf of skills into something you can switch off as a group
When I sorted my skills by what work they belong to, the set I open almost daily turned out to be 5 percent of the total. Here is how I dropped a .claude-plugin/plugin.json into each group so I could switch a whole shelf off, plus the two loading rules I tripped over on the way.