Reeler: the spec
Unit 24 · Project 4 (Reeler, a Letterboxd clone). This is the first project on the Pair rung of the AI-usage ladder, and the first with an AI checkpoint — a milestone where you review Claude-produced code for a seeded flaw. Read this spec before writing any code.
Reeler is a film-logging app: search a film database, open a film's page, and keep your own record of it — a star rating, a like, a watchlist, a review, and a diary of what you've watched — with a profile that totals it all up. It's the personal half of Letterboxd: the social graph (following, an activity feed of other people) is deliberately later, higher-tier work. You build it to lock in the list-detail spine and a real SwiftData model where several screens are just filtered views of one store.
What you are building
- Search — a debounced search field, a results list, and empty / loading / error states, into a film detail page (poster, director, genres, runtime, overview).
- Log a film — from the detail page: a tappable star rating (half-stars), like, watchlist, mark watched, and a review — all persisted.
- Diary — your watched films in reverse-chronological order, with rating and date.
- Watchlist — the films you want to see, as a poster grid.
- Profile — stats (films watched, this year, likes, average rating) and your lists.
What it looks like finished
FilmActivity store. (Posters render as colour cards here — a real build loads them from the film API through the actor image cache.)Concepts this project locks in
| Concept | Where it shows up |
|---|---|
| MVVM-lite + dependency injection | SearchViewModel takes an APIClient protocol |
@Observable @MainActor view model |
drives the search state |
| Debounced async search | Search screen, .task(id:) + Task.sleep |
Value-based NavigationStack |
navigationDestination(for:) with a Film value |
| Load-more pagination | Search results append the next page |
| Actor-based image cache | ImageLoader actor, no data races on the cache dictionary |
| SwiftData model + relationship | @Model FilmActivity, FilmList ↔ ListEntry |
| Views as filters over a store | Diary / Watchlist / Likes are @Query filters over FilmActivity |
@Bindable editing |
rating, like, watchlist, review write straight to the model |
The AI stage: Pair
Projects 1–3 were Tutor — AI gave hints, never code. Reeler moves you to Pair: the AI may propose code, but you review and approve every diff. Nothing lands unread. This is the professional default, not a training wheel — a senior engineer reads the diff their tools produce, and the bugs that survive review are the ones nobody looked at.
The /swift-review habit. From this project on, run /swift-review on any non-trivial diff
before you accept it. It checks the diff against Segue's conventions: strict concurrency, correct
Sendable, @MainActor placement, capture lists, and tokens-only styling. In Pair mode the
review is yours to run — the Workbench never lands a change you didn't look at.
Milestones
- Search + MVVM — the search screen, an injected
APIClientprotocol, a@MainActor@Observableview model, debounced queries. - Detail + pagination — value-based navigation to a detail screen, load-more results, and an actor-based image cache.
- Rate, log & profile — the SwiftData
FilmActivitymodel; the detail's rating/like/ watchlist/review; and the Diary, Watchlist, and Profile screens as filters over the store. - AI checkpoint — review a Claude-produced image loader for a seeded retain cycle: find it, explain why it leaks, fix it. Then a gradable companion exercise.
- Rubric — self-review against the rubric and defend your design decisions.
Out of scope (on purpose)
No authentication and no server of your own (you consume a public read-only film API), and no social graph — following, an activity feed of other people, comments and likes on their reviews. That's Tier 2+ work in later projects. Reeler's job is the personal-logging spine: search, log, and the views that read back your own data. A tight scope you finish beats a broad one you abandon.
Knowledge check
Q: What changes about how you use AI on this project versus the first three?
The stage moves from Tutor to Pair. AI may now propose code, but you review and approve every
diff — nothing lands unread. /swift-review becomes a habit on non-trivial changes.
Q: Why are the Diary, Watchlist, and Likes not three separate stores?
They're three filters over one FilmActivity store — logged, on the watchlist, liked. One
source of truth means a rating you set on the detail page shows up in the diary automatically,
with nothing to keep in sync.