I took over a 50,000–60,000-line React Native codebase with no documentation and a storage bucket that exposed every user's photos. I fixed the exposure in my first week, shipped a new measurement tool inside the existing code, then led the native iOS rebuild: 581 Swift files, about 104,000 lines, and 303 SwiftUI views.
The Redesign
The legacy React Native app on the left, the native SwiftUI rewrite on the right.


The Product
HornScore is a hunting app for serious hunters. Users photograph trail-cam footage, field-judge antler measurements using anatomical references, calculate official scores adapted from Boone & Crockett methodology, share scores with a community, and access species-specific guides.
The audience invests in premium equipment and expects the software to match. This is a production app with 5,000+ users.
The App
Every flow in the production app (feed, scoring, field judging, guides, and community) uses the same design tokens. The steps below follow the three scoring tools, recorded in the app, and the scoresheet they produce.
01 · HornMatch
You compare your animal with already-scored animals and mark it bigger, smaller, or similar. Each answer narrows the range. In this recording, five swipes take the estimate from 103–271 inches to 189–198.
02 · HornTrace
You set a known reference, then draw along the tine. The app measures the line against the reference as you draw and enters the result in the scoresheet.
03 · HornCalc
Each part is measured against reference photos of scored animals. One tap copies a side's measurement to the other side, and the total updates with every entry.
04 · Harvest breakdown
Mass, length, and point measurements, in a simple or advanced view, so anyone can see how a score was reached.


New users see these before signing up. Scroll sideways to see all six, or tap one to enlarge it.
Where It Started
In my first week, before building any features, I audited the repository and wrote up the findings for the founders. These were the main ones.
Codebase audit · week one
Lines of undocumented React Native
Half-built versions of the app, in parallel
Tests, docs, or versioning
Lines in a typical file
The security finding
The Firebase storage bucket was publicly readable: trail-cam photos, hunting locations, and personal images, open to anyone with the URL. I documented the exposure, wrote proper access rules, and verified the fix before touching anything else.
Fixed in week one, before any feature work.
Phase 1
Before proposing a rebuild, I shipped the Line Reference Tool inside the legacy React Native codebase. I wanted to show that substantial features could still ship in the existing code, on schedule, without breaking anything.
That first version measured straight lines only: you calibrate against an anatomical reference, then draw. Delivering it is what earned the trust to propose the rebuild.
The original Line Reference Tool in the legacy app (straight lines only).
Phase 2
I started the rebuild on November 6, 2025, in native Swift and SwiftUI. Today it is a ports-and-adapters architecture: MVVM screens, a core of use cases and repositories with no vendor code in it, one place where every dependency is wired, and a design system meant to scale. I was the only engineer on it.
The shareholder progress report rated technical risk as low. The remaining work is feature development, with no further re-architecture planned.
Architecture
The app is built as ports and adapters. Scoring rules, feed logic, and every use case live in a core package that imports nothing from Firebase, RevenueCat, or Sentry. The core talks to the outside through 82 small protocols, the ports, and an adapter on the other side of each one does the real work.
Pick an action and follow it through. Then take the phone offline, or switch to the test doubles, and see what changes.
HomeScreenV2The home screen asks for the feed.
Type names, paths, and offline behavior are from the app's source. Subscriptions, error reporting, and analytics plug in through ports of their own in the same way; they're left off the drawing because none of these four actions pass through them. Sign-in, the paywall, and account settings still run on the older wiring and are the last pieces being moved over.
The Unified Tool
The legacy app had separate modes for straight and curved measurements, and users had to know which one they needed.
The rebuild replaces both with a single canvas state machine. The first two points calibrate against an anatomical reference, and adding more points turns the line into a curve. There is no mode switch.
In code, it is a seven-state enum with a transition function that advances automatically. The old states remain, marked `// Legacy — should not be entered in unified flow`.
Points stay on the antler while the user pans and zooms to 15×. That depends on two transforms between canvas space (image pixels) and screen space (the rendered SwiftUI view). Every touch, drawn point, and hit-test goes through them.
From FieldJudgeScoringViewModel.swift (2,089 lines), which holds the coordinate math.
func screenToCanvas(_ screenPoint: CGPoint, viewSize: CGSize,
totalScale: CGFloat, totalOffset: CGSize) -> CGPoint {
let center = CGPoint(x: viewSize.width / 2, y: viewSize.height / 2)
return CGPoint(
x: (screenPoint.x - center.x - totalOffset.width) / totalScale + center.x,
y: (screenPoint.y - center.y - totalOffset.height) / totalScale + center.y
)
}The curve is a Catmull-Rom spline whose length is summed over 200 intervals per segment, recomputed in real time as points move. The 44pt tap threshold is converted to screen space for hit-testing, and stroke width scales inversely with zoom so lines stay visible without thickening.
Design System
I built the design tokens before any feature screens: adaptive light and dark colors, semantic surfaces, a brand color that adjusts to the color scheme, and spring curves that match the native iOS sheet animation. Every screen in the app uses this file.
DesignSystem.swift · 586 lines
01 · Color
50+ tokens
02 · Type
20+ sizes
03 · Spacing
25 tokens · 4pt grid
DesignSystem.swift: 586 lines, dark-mode aware, used in 165 production files.
The Stack
Authentication, analytics, crash reporting, and subscriptions were integrated and tested before I built any feature screens.
Working Together
The apps businesses depend on usually carry far more than one feature: a feed, photo and video uploads, tools people use out in the field, offline access, subscriptions sold through the App Store and the web, and analytics that show what's working. If your app has that many moving parts, I'd take the same approach I took here. In the first week I audit the codebase for security and architecture risks. While we plan, I ship improvements inside the code you already have. I recommend a rebuild only if the findings support one, and if they do, you get one person who designs the screens, writes the Swift, and owns the backend behind it.