Case study / Consumer mobile
Tally Ho: a card-night score keeper shipped solo to the App Store
7 days from first commit to App Store approval
- Client
- Personal product
- Industry
- Consumer mobile
- Timeline
- 2 weeks, Mar 2026 (first commit 3/14, App Store 3/21, v1.1 live 3/28)
- Services
- Mobile app, MVP build
- Stack
- React NativeExpo SDK 54TypeScriptexpo-sqliteMultipeer ConnectivityReact NavigationReanimatedJestPlaywrightEAS Build
- Results
- 2 releases in 2 weeks (v1.0 on 3/21, v1.1 on 3/28)2–10 players per game, each on their own device over P2P4 layout modes: iPhone portrait/landscape, iPad portrait/landscape0 data collected, analytics, or internet requests26.8 MB App Store download size
- Status
- live
The problem
Oh Hell is a trick-taking card game where every round you bid how many tricks you will win, and the score depends on hitting the bid exactly. Scoring it on paper means tracking bids, tricks, and running totals for up to ten players across a dozen or more rounds while the deal size changes and the dealer rotates. Someone always ends up doing arithmetic instead of playing.
I wanted a scoreboard that sits in the middle of the table, readable from across it, that handles the rules so nobody has to. I also wanted to prove I could take a mobile app from an empty directory to the App Store alone, with an architecture where adding a second game costs almost nothing.
Constraints
- One person, nights and weekends. No designer, no QA, no one else to handle App Store Connect, screenshots, or the privacy questionnaire.
- Table use. Controls large enough to tap without looking closely. The iPad in the center of the table is the primary surface, but the same codebase has to feel native on a phone.
- Multiplayer without infrastructure. Each player enters their own bid and result on their own phone, with no server, no accounts, and no reason to collect anyone’s data.
- Honest privacy posture. The App Store label had to say “no data collected” and be true. No analytics, no crash reporting, nothing that phones home.
- Native modules on Expo. Peer-to-peer networking needs native code, so Expo Go cannot run it. Every multiplayer test needs a development build on physical devices.
What I built
Tally Ho is a React Native / Expo app in TypeScript on the iOS App Store under my own developer account. The first module is a full Oh Hell score keeper: full and half game modes, bid tracking with over/under validation, trick-total verification, automatic scoring (10 points plus tricks for a made bid, tricks only otherwise), per-round history, save and resume, saved player groups, win tracking, and a built-in rules reference.
The architecture is a plugin registry. Each game lives in src/games/<name>/ with its own types, reducer, screens, and components, and registers one GameDefinition. Navigation is built dynamically from the registry, so a new game appears on the home screen with no edits to the app shell. Game phases (order, bid, play, scored, game over) render conditionally inside one screen rather than as separate routes, so an accidental back-swipe cannot wipe a game in progress.
Persistence is expo-sqlite behind a StorageProvider interface, with versioned game state and migrations. v1.1 added a dealerIndex field and removed a phase; v1.0 saves migrate forward automatically, and the migration is tested.
Local multiplayer uses Apple’s Multipeer Connectivity through expo-nearby-connections, over Wi-Fi or Bluetooth with no internet. The host runs the game reducer as the single source of truth and broadcasts state snapshots; peers render the snapshot and submit their own bids and results. The transport sits behind an adapter interface, and a MockAdapter exercises the host/peer logic in Expo Go without hardware. A Host/Peer context split leaves solo mode untouched by the multiplayer code.
For iPad, every dimension flows through scale() and moderateScale() so controls size up on the tablet without breaking phone layouts. Stepper buttons went from 36 px to 64 px (roughly 90 px on iPad) in v1.1, action buttons got a 52 px minimum height, and the scorecard modal got wider columns. iPhone is locked to portrait; iPad supports all four orientations.
The release process was mine end to end: EAS Build profiles for development, preview, and production; Playwright screenshots at four viewport sizes for the required iPhone and iPad slots; a hosted privacy policy; the App Store Connect listing; submission. Every UI change was reviewed on an HTML before/after comparison page, which is how I caught layout regressions without a second pair of eyes.
Architecture
Shared components use a createStyles(theme) pattern, so there are no hardcoded colors and a second game inherits the look for free.
Results
- v1.0 approved and released 2026-03-21, seven days after the first commit. v1.1 shipped 2026-03-28 with the iPad scaling overhaul, dealer tracking, a merged play/result phase, undo scoring, and the rules reference.
- Listed in Games / Family / Card, rated 4+, iOS 15.1 or later, iPhone and iPad universal, 26.8 MB.
- Privacy label: no data collected. No network requests except local peer-to-peer during a multiplayer game, and the policy page says exactly that.
- Jest suites cover the reducer, scoring, round generation, and state migrations; because the reducer is the source of truth, the same tests exercise solo and multiplayer paths.
- Adding the next game is a new folder plus one registry entry.
App.tsxdoes not change.
Screenshots for the site (paths relative to the tally-ho repo): screenshots/appstore/iphone_6.7_setup.png, iphone_6.7_gameplay.png, iphone_6.5_setup.png, iphone_6.5_gameplay.png, ipad_12.9_setup.png, ipad_12.9_gameplay.png; flow captures screenshots/comparison/final-iphone-{1-home,2-setup,3-order,4-bid,5-play,6-scored}.png and final-ipad-{1-home,...,6-scored}.png; side menu v3-tp-menu.png, v3-ip-menu.png; device photos screenshots/IMG_0052.PNG, IMG_0053.PNG.
What this proves
- Mobile: I can carry an iOS app from empty repo through native-module integration, device testing, App Store Connect, privacy disclosure, and review, alone, in a week, then iterate on feedback a week later.
- MVP Sprint: the plugin registry, storage interface, and transport adapter are the seams that make a v1 cheap to extend instead of cheap to throw away. The second game is a folder, not a rewrite.
- AI Integration (adjacent): built with Claude Code as the second pair of hands, with the review discipline (comparison pages, migration tests, mock adapters) that keeps AI-assisted speed from turning into regressions.
Similar project?
Have an app idea that needs to be on the store in weeks, not quarters? Book a 20-minute call.
Published Oct 1, 2026