Building Piton
Solo build Personal Apr – May 2026 · TestFlight
- Swift
- SwiftUI
- SwiftData
- HealthKit
- CoreMotion
- WatchConnectivity
- AppIntents
Piton is a personal-use climbing tracker for Apple Watch and iPhone. I started it because every climbing app I tried treats failed attempts as a bug. The data point I most wanted recorded was the one no app would keep. It’s now in TestFlight, build 1.0(2) processing, app name "Piton Climbing", record live in App Store Connect.
The wedge is the climber as a co-pilot, not a passenger. KAYA is a logbook. Redpoint counts meters. Crimpd hands you a training plan. They each conflate two different questions: "how am I training?" and "am I getting closer to sending this?" Neither gets answered well. Piton answers both, in their own surfaces, with honest data.
Structural choices fall out of that wedge. Failed attempts are first-class. The auto-detect is transparent and tunable. When it fires, the Inspector shows why and the climber can override with a single tap. Harness plus chest strap is supported, not assumed to be wrong. Per-gym detector profiles, because Pacific Edge HVAC is not Movement Belmont, and one-size-fits-all altitude detection produces inconsistent laps.
The architecture is two apps backed by one Swift package. PitonCore holds the SwiftData schema, the sync envelope types, and the detector itself: a pure function that takes sensor samples and a profile and returns detected laps. The same code runs live on the watch and against replayed data in the phone’s Inspector. The watch is the primary source of truth. The phone is an explicit fallback for the forgot-the-watch or dead-battery day, stamped with captureMode = .phone so metrics never lie about provenance.
Sync between devices is batched WatchConnectivity envelopes via transferUserInfo, not per-record sendMessage in a loop. That single decision skipped weeks of "messages dropped while the phone was locked" debugging. Conflict resolution is last-write-wins on updatedAt. Phone-originated edits like grade tags, route assignments, and notes flow back to the watch on next reachability, with a 60-second reachability-handler cooldown to avoid flap-bursts.
The detector lives in the package as a state machine over a 1 Hz altimeter feed and 50 Hz device motion. Enter, climb, top, descend, with hysteresis tuned per gym. Two sprints in, I learned the hard way that the watchOS crown press is a system gesture that can’t be intercepted by app code. So the watch grade picker has an explicit Set button instead of trying to be clever. The other thing I learned: bouldering auto-detect is out for v1. Boulder altitudes are too short for the state machine to distinguish a top from a fall, and the failure mode is silent wrong data.
The AI piece is intentionally narrow. Coach’s Notes, weekly synopses, and monthly synopses observe. They never prescribe. The constraint is in the prompt, in the system messages, and in the fallback copy. Climbing media has been blunt that serious climbers reject prescriptive AI training advice, and trying to be the coach is the fastest way to lose them. Piton’s voice is a training mirror. "Here’s what stood out" is allowed. "You should do X" is not. The activity rings on the watch are the model: tell you what you did, not what to do.
The phone information architecture collapsed in May. The earlier four-tab structure was wrong. Piton has one domain, climbing data, sliced by time. Today / Week / Month / Lifetime as a single screen with a time toggle replaced it. Projects and Climbs and Stats and Trips all dissolved into the right time scale. Today is a status check. Numbers are status, graphs are stories. The altitude graph is interesting but not glanceable, so it moved to session detail by deliberate choice.
Where it sits today. PitonCore is at 273 of 273 tests passing across 32 suites. Privacy manifest published, declarations matching what flows to Anthropic. iOS and watchOS release builds clean. TestFlight build 1.0(2) processing. The first build was rejected by Apple’s keyword filter for the word "Apple" inside the MarkLapIntent description (ITMS-90626), which trips even on "Apple Watch", so the description got rephrased. Internal testing group of one member, me. Next session is post-install verification and the custom-icon design pass.
The interesting work was deciding what not to do. No bouldering auto-detect. No outdoor route database. No iCloud cross-device sync. No social features. No leaderboards. No prescriptive coaching. Each refusal earned the surfaces that remain.
A climbing app is a behavioral mirror, not a behavioral lever. The job is to show the climber their training arc accurately enough that they can decide what to do next. The app that tries to decide for them is the one I keep deleting.
Tags
- iOS The device people actually carry. No second screen.
- watchOS On-wrist, glanceable, sensor-rich. The most under-appreciated platform on the market.
- SwiftUI Declarative UI for Apple platforms. The right level of abstraction for shipping fast.
- Apple Watch Small screen, short attention, sensors that don’t lie. Forces clarity on what matters.
- AI Product Design Designing for models that are good, wrong, or weird. Often on the same screen. I care more about trustworthy and unobtrusive than flashy.
- Sensor Fusion Altimeter plus accelerometer plus gyro, combined into something that says what’s actually happening.
- Information Architecture The shape of the thing before the pixels. Get this wrong and nothing else lands.
- HealthKit Apple’s health data layer. Honest about consent, strict about storage.