PiehSoft
← All work

HornScore

5,000 hunters, zero security debt

HornScore product render

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.

Timeline
Sep 2024 → present · Native rebuild Nov 2025
Role
Lead Engineer & Designer
Technology
Swift, SwiftUI, Firebase, Cloud Functions
Platform
iOS native
5,000+
Production users
104K LOC
Native Swift codebase
15× zoom
Coordinate-locked precision

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.

Home feed
Home feed
Score detail
Score detail
Community
Community
Comments
Comments
Profile
Profile
Species selector
Species selector
Scoring mode
Scoring mode
Field judge modal
Field judge modal
Line Reference Tool — Bézier measurement
Measurement tool

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.

audit findings · week 01
  • 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
The security finding

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

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.

581
Swift files
across the repo
104K
Lines of Swift
production code
303
SwiftUI views
156 feature-specific
39
Domain entities
modeled and validated
87
User-facing screens
across 12 feature modules
5
Backend integrations
Firebase, RC, Amplitude, Sentry, FB

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:

swift
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.

HornScore markHornScore

DesignSystem.swift · 586 lines

01 · Color

50+ tokens

RGB(0.745, 0.518, 0.365)
Brand primary
#065767
Brand accent · Light
#1E92A8
Brand accent · Dark
Adaptive
Surface · Dark

02 · Type

20+ sizes

Display 64HornScore
Title 32Field judge accurately.
Body 18Score trophies the right way.
Caption 12ANTLER POINTS · 0.12EM

03 · Spacing

25 tokens · 4pt grid

4pt
8pt
12pt
16pt
20pt
24pt
32pt
48pt
64pt
50+
Color tokens
25
Spacing tokens
20+
Type sizes
40+
Frame sizes

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.

Firebase
Firebase
Auth, Firestore, Storage
Google Cloud
Cloud Functions
TypeScript · FCM, provisioning
RevenueCat
RevenueCat
Subscription billing
A
Amplitude
Product analytics
Sentry
Sentry
Crash & error reporting
Apple
Sign in with Apple
Nonce-based OAuth
Google
Google
OAuth · auto-login
Facebook
Facebook
OAuth · email verification

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 →