How I Build iOS Apps With Claude Code: One Session, One Task
Cascade went from a game my son invented to the App Store in about five weeks. Claude Code wrote most of the code, while I also shipped iPhone Duo updates for my other apps.
People ask how. The honest answer isn't a clever prompt. It's one habit: every session does one task, and only one.
This post is the whole workflow, using Cascade as the example.
Three places, three jobs
I use Claude in three places, and each one has a different job.
- Chat is where decisions happen. Rules, scope, design direction, what goes in 1.0 and what waits. Nothing gets built here. When a decision is made, it gets written down.
- Claude Code in the terminal is where the work happens. One tab per task, often several running at once on separate branches.
- The Claude agent in Xcode is for anything I need to see. Previews, the simulator, motion. Visual iteration is faster when the agent and I are looking at the same thing.
Keeping these apart stops the expensive mistake: making product decisions halfway through an implementation, in a session whose job was something else.
Write the rules down before the code
When I started, the rules lived in my son's head and in an early version of the app that didn't quite match them.
So the first session wasn't a feature. It was rules canonization: turn the game into a RULES.md at the repo root, make the engine match it, and back it with tests. Two rulings were still open at the end of that session. I settled them with my son.
From then on, RULES.md was the source of truth. When a rule changed later (we dropped rounds entirely, so one deal is one game), the document changed first and the code followed. Design got the same treatment with a DESIGN.md holding the palette, type and component rules.
These files do more for consistency than any prompt. Every new session reads them, so every session starts from the same facts.
One session, one task
Here's roughly how Cascade's build was split. Each line was its own session and its own branch:
- Rules canon.
RULES.md, engine fixes, tests. - Joker UI. The one move the board couldn't express yet.
- Turn driver. Fixing a stall where the CPU stopped taking turns.
- Player identity. Board labels driven by player data, not hard-coded.
- Game modes. Solo versus pass and play.
- Components, then layout. The visual restyle, then adaptive one-page and two-page layouts.
- Then the rest. Hidden hands, feedback, persistence, accessibility, launch polish.
Why it works:
- The context stays small. A session that only touches the turn driver doesn't need to know about card art.
- The diff is reviewable. One task means one coherent change. I can read it properly before it merges.
- Sessions can run in parallel. With git worktrees, two sessions work on two branches without stepping on each other.
- Failure is cheap. If a session goes sideways, I throw away one branch, not a week.
The temptation is always "while you're in there, also fix…". Don't. Write it down for the next session instead.
End every session with a handoff
That "write it down" is the part people skip, and it's the part that makes the chain work.
Every session ends by listing what it noticed but didn't touch, because it was out of scope. Two real examples from Cascade:
- The player identity session noticed that in a two-human game, the local seat was pinned to the first player. Pass and play would need the shown hand to follow whoever's turn it is. That became part of the game modes session.
- The game modes session noticed that pass and play swaps the board every turn. That's right on one phone being handed across a table, and exactly wrong on a two-page layout where each player keeps their own side. That note went straight to the layout session, and it's the same bug I wrote about in designing for iPhone Duo.
Neither was fixed where it was found. Both were fixed on purpose, in the session that owned them.
Green or it doesn't merge
Every session ends with the full test suite passing. Not the tests for the new code. All of them. Cascade's suite grew session by session, and nothing merged red.
Tests cover the engine. For the things you have to see, I added a debug-only launch argument that drops the app straight into a specific board state:
-fixture jokerChooser
-fixture passAndPlay
A rare endgame position that could take many games to reach by hand is one launch away. Every later session reused them, for testing, screenshots and checking layout.
Then I play it myself. My first solo playtest found that the layout felt squished and that the CPU's legal back-to-back endgame turns felt like extra turns, because you couldn't see how many cards it had left. No test would have caught either. Both became sessions.
Put what you repeat into skills
When I find myself explaining the same thing to a third session, it becomes a skill: a short file of instructions Claude Code loads when the task calls for it.
I keep a skill for how my apps do onboarding, so every app follows the same pattern without me re-explaining it. Xcode also ships its own agent skills for SwiftUI, accessibility and testing. I copy those into ~/.claude/skills so terminal sessions get them too, and re-check them after every Xcode update.
What Claude didn't do
Everything so far is about the code. The code was never the hard part of Cascade.
Claude didn't invent the game. My son did, and he still has the final word on the rules. When a question came up, like whether your opponent's joker can steal a locked set of four, I asked him.
Claude didn't decide what was fun. The best moment in Cascade is the standoff near the end, when both players are holding cards and waiting for the other to blink. Protecting that shaped almost every decision, including how the CPU plays. A CPU that grabs every set it can kills the standoff.
Claude didn't choose the look. I shipped a complete visual style, lived with it for a week, decided it felt too classic, and threw it out. Three rounds of mockups later, I landed on the one you see today.
And Claude didn't playtest it. That was me, playing until something felt off, then working out why.
What the workflow buys me is time for those parts. They're the ones people notice.
If you try one thing
- Write a
RULES.mdorDESIGN.mdfor your app. Whatever your source of truth is, put it in the repo. - Give your next session exactly one task, on its own branch.
- End it with a list of what it noticed but didn't touch. That list is your next session.
- Don't merge until everything is green and you've used the thing yourself.
None of this is specific to Claude, or to iOS. It's how you'd want to work with any capable collaborator who starts each day with no memory of yesterday. Give them the facts, a single job, and a clear way to hand over. Then spend the time you save on the parts only you can do.
Cascade is on the App Store for iPhone and iPad.