Project 1 — Counter+ / Tip Splitter: the specswift-6.4/ios-26
Lesson 1 / 4
Unit 21 · Counter+ / Tip Splitter

Project 1 — Counter+ / Tip Splitter: the spec

Note

Unit 21 · Project 1 (warm-up). Backend Tier 0 (no persistence). AI stage: Tutor — you type every line by hand; /tutor gives hints, never code. This spec lesson defines the target; the milestone lessons build it.

This is the shortest project on the ladder, and that is the point. Before you touch SwiftData, networking, or navigation, you prove you can drive SwiftUI's core loop: state goes in, a view renders it, an interaction changes the state, the view updates. Everything harder is built on that loop. You will build two small screens that share nothing but the same discipline.

What you build

Counter+ — a single screen with a large number, a + and button, an adjustable step size, and a reset. It clamps at a floor of zero. It is the "hello world" of @State.

Tip Splitter — a bill calculator: enter a subtotal, choose a tip percentage, set the party size, and see the tip amount, the grand total, and the per-person share, all formatted as currency. Every output is a computed value derived from the inputs; you store nothing you can calculate.

Both screens live in one app behind a TabView. That is the entire surface area.

The screens

Screen Inputs Outputs
Counter+ + / buttons, step stepper, reset current count
Tip Splitter subtotal field, tip % control, party size tip, total, per-person

What it looks like finished

Counter+ at rest — count 0, minus and Reset disabled Counter+ mid-count — 7 with a step of 2 Tip Splitter empty state — a prompt instead of a wall of zeros Tip Splitter — a $73.40 bill at 20% split three ways: $14.68 tip, $88.08 total, $29.36 each
The finished build, running in the simulator: Counter+ at rest and mid-count with a custom step, and Tip Splitter's empty state and a $73.40 bill split three ways at 20% ($14.68 tip, $88.08 total, $29.36 each). This is the target — the milestones build it.

Concepts this locks in

  • @State as the single source of truth for a view's own data.
  • VStack / HStack / Spacer layout and alignment.
  • Button actions that mutate state, and disabling a button by state.
  • Computed properties on a view struct for derived values.
  • TextField with a numeric binding, and .formatted(.currency(code:)) for money.
  • Designing empty and edge states on purpose (subtotal blank, party size of one).

Backend tier: 0

Nothing persists. Close the app and both screens reset. That is intentional — persistence is Project 2's job. Resist the urge to reach for @AppStorage here; you are practicing the in-memory state loop cleanly first.

AI stage: Tutor

You are in the most restrictive AI mode on the ladder. You hand-type all code. When you are stuck, run /tutor and describe the specific wall you hit — a compiler error, a layout that won't center, a binding that won't update. Tutor responds with a question or a hint that points at the concept, not a snippet you paste. The goal of Project 1 is that the SwiftUI mental model becomes yours, not the model's.

Tip

A good /tutor prompt is specific: "my count label doesn't update when I tap +, here's my Button action" beats "my counter is broken." The narrower the question, the sharper the hint — and the more you learn from closing the gap yourself.

In scope

  • Two working screens in a TabView, light and dark mode both correct.
  • Currency formatting that respects the device locale.
  • Sensible disabled/edge states (no negative count, no divide-by-zero).
  • VoiceOver labels on the interactive controls.

Out of scope

  • Persistence of any kind (Tier 0).
  • A view model or @Observable type — a view's @State is enough here, and Project 2 earns the upgrade.
  • Networking, navigation stacks, multiple currencies, split-by-item.
  • Animations beyond SwiftUI's implicit defaults.

Definition of done

You can hand the app to someone, they can count up and down with a custom step, split a $73.40 bill three ways at 20%, and every number is right, formatted, and readable in dark mode. When that is true, move to the rubric and defend your choices.

Knowledge check

Q: Why is there no persistence in this project? Tier 0 is deliberate. Project 1 isolates the in-memory state-to-view loop so you learn it without the confounder of a storage layer. Persistence arrives in Project 2 with SwiftData.

Q: In Tutor stage, what should /tutor give you? A hint or a guiding question aimed at the concept you're stuck on — never finished code to paste. You type every line yourself so the SwiftUI model becomes yours.