Inherited 60,000 lines of undocumented React Native and a security vulnerability exposing every user's photos. Fixed it in week one, shipped Phase 1 inside the legacy code, then led the full native iOS rebuild — 581 Swift files, 104K LOC, 303 SwiftUI views.
The Product
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.
Screens
Eighty-seven screens, one design system.
Every flow in the production app — scoring, filters, settings, social, guides, messaging, comments, albums, reports — built from a single tokenized system. A representative selection is shown below.









What I Inherited
Week one: audit findings.
Before writing a single feature, I documented what was actually in the repository. Here's the report I wrote to the founders.
- 0150,000–60,000 lines of React Native
- 02Three partially implemented versions of the app, in parallel
- 03No versioning. No tests. No documentation.
- 04Individual files averaged 2,000–5,000 lines each
- 05Firebase storage bucket was publicly readableCRITICAL
Every user's trail-cam photos, hunting locations, and personal images were accessible to anyone with the URL. I documented the exposure, implemented proper Firestore rules, and verified the fix before touching anything else.
Phase 1
Phase 1 — ship inside what already exists.
Before proposing a rebuild, I shipped the Line Reference Tool inside the legacy React Native codebase. The goal was simple: prove that complex new functionality could be delivered in the existing code, on time, without breaking anything.
It was the work that earned the trust to propose the bigger move.

Line Reference Tool, shipped inside the legacy code.
Phase 2
Phase 2 — the native rebuild.
First commit on the rebuild branch: November 6, 2025. Native Swift, SwiftUI, MVVM + Coordinator architecture, dependency-injection container, repository pattern, design system built to scale. Owned by one person.
Risk assessment from the shareholder progress report: technical risk LOW. The remaining work is feature implementation, not re-architecture.
The Math
The math underneath the measurement tool.
The Line Reference Tool lets users place Bézier curve points on a photo and trace anatomical landmarks at up to 15× zoom — with coordinate-locked precision, even as the user pans, zooms, and re-zooms.
The trick is two bidirectional transforms between canvas space (image pixels) and screen space (the rendered SwiftUI view). Every touch event, every drawn point, every hit-test runs through these:
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
)
}↳ FieldJudgeScoringViewModel.swift — the coordinate-space math core (2,089 lines).
↳ DetailThe Bézier rendering uses a Catmull-Rom spline with arc-length computed by parametric subdivision (200 intervals per segment), integrated in real time as the user adjusts points. Hit-testing dynamically adjusts a 44pt tap threshold to screen space, and stroke width scales inversely with zoom so lines stay visible without thickening.
The Unified Tool
Two tools became one.
The legacy app had separate straight-line and curved-line measurement modes — two interaction models the user had to keep in their head.
The rebuild collapses both into a single canvas state machine. The first two points calibrate against an anatomical reference; three or more extend into a curved measurement. The transition is automatic; users never see a mode toggle.
The code is just as direct: a seven-state enum, an auto-advancing transition function, and comments throughout marked `// Legacy — should not be entered in unified flow`.
Design System
A 586-line design system, before a single feature shipped.
I built the tokens first. Adaptive light/dark, semantic surfaces, a brand color that adjusts to color scheme, spring curves that match native iOS sheet snap. Every screen in the app draws from 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 throughout, used across 165 production files.
The Stack
Production infrastructure, wired before the UI.
Auth, analytics, crash reporting, subscriptions — all integrated and verified before a single feature screen was built. Standard practice in mature engineering organizations; rare in solo work.
Why this matters
Same problem, different vertical.
If your business runs on an iPhone app that captures field data and syncs to a server, this is the same problem shape. The track record I'd bring: audit your codebase for security and architecture risk in week one, ship value inside the existing code while we plan, and only propose a rebuild when the data — not the engineer — calls for it.
william@piehsoft.com →