iOS & watchOS · Active development · Internal TestFlight
Rallying
A multi-sport racket tracker for iPhone and Apple Watch — record matches rally-by-rally on the wrist, review them later.
For racket-sport players who want to record matches on Apple Watch and review performance over time, across squash today and more sports as it grows.
Feb 2025 → Present · iPhone & Apple Watch · iOS 17+ / watchOS 10+
Rallying is a match companion for racket-sport players, built around scoring a game live on Apple Watch or iPhone. Squash is the first sport supported, with tennis, padel, pickleball, badminton, table tennis and racquetball planned on top of the same scoring model — singles or doubles, score-only or rally-by-rally, whichever a player prefers.
Playing a match also starts an Apple Health workout session, so heart rate and active energy are captured for the same window the score was recorded in and shown alongside it afterwards. A shared recording controller keeps the watch and phone in step, and match history, player and location management, and detailed statistics sync across devices via CloudKit.
Rallying grew out of Squash Tracker, the squash-only predecessor that proved the slot-based scoring model over more than a year of real matches. Its final release rewrote the schema from the ground up, and that rewrite became Rallying’s foundation rather than a dead end — existing Squash Tracker testers can carry their match history across via CSV export and import.
Features
- Multi-sport tracking (squash today, more racket sports planned) with singles or doubles and score-only or rally-by-rally recording.
- Live match recording on Apple Watch or iPhone, with a shared recording controller keeping both in sync.
- Heart rate and active energy capture via HealthKit workout sessions during play.
- CloudKit sync across devices, with CSV import to carry Squash Tracker match history across.
- Spoken score announcements and detailed match statistics carried over from the squash-only predecessor.
Built with
- SwiftUI
- SwiftData
- HealthKit
- WatchKit
- CloudKit
- Swift Charts
Development log
-
1 February 2025
First commit — the idea
A watchOS companion app scaffold, with the phone app to follow.
Read more
Squash Tracker began on the watch: an initial commit with basic data structures and a navigation stack, built to run standalone before a phone companion existed. Within three weeks a second target was added for the iPhone app, alongside a CloudKit container to eventually carry match data between the two. The earliest UI work was mostly navigation and onboarding scaffolding — nothing that could score a rally yet.
-
4 March 2025
Rally-by-rally scoring and server tracking
The core mechanic arrives — recording who served, who won the rally, and the score that follows.
Read more
The data model grew a proper scoring method, with explicit selection of the first server and the opening server for each match and game, and server-side tracking that updated automatically as rallies were logged. Fixing custom-enum handling for CloudKit came early, a reminder that SwiftData's CloudKit mirroring is stricter about type shape than local persistence alone. By early March a match could be scored rally by rally with the correct side and points display, the foundation everything else was built on.
-
16 May 2025
HealthKit workout sessions and a heart-rate chart
Playing a match becomes a tracked workout, with heart rate charted alongside the score.
Read more
A workout session tracker was added so that scoring a match on the watch also recorded it as an Apple Health workout, complete with heart rate and active energy. It shipped rough in March — the first version was described in its own commit message as "working but buggy" — and ending a workout cleanly was hardened over the following weeks: async session handling, nil-safety checks, and, by mid-May, a confirmation step to stop a workout being ended by accident. A heart-rate chart on the match detail view, hidden automatically for imported matches without timing data, was the payoff for getting the session lifecycle right.
-
30 May 2025
Spoken score announcements and a proper stats screen
The watch speaks the score after each rally, and match statistics grow milestones and trends.
Read more
Text-to-speech announcements were added so a score update could be heard rather than read mid-rally, with a toggle to turn them off and several rounds of fixing audio clashes and crashes along the way. In parallel, the statistics side of the app matured well past a simple win-loss count: combined stats-and-milestone views, sortable and filterable match lists, and a trend indicator showing whether a given stat was rising or falling over recent matches.
-
30 June 2025
Fixing "Me" in score announcements, and more flexible match setup
Announcements learn to say your name properly, and match creation gets faster with remembered defaults.
Read more
A recurring naming bug in the spoken announcements — the app referring to the player as "Me" instead of their name, producing lines like "Me's service from left" — was traced to a first/third-person mix-up and fixed properly rather than patched around. Match setup became more flexible soon after, in early July: a "best of" target became optional (falling back to an open-ended match), and new-match options now prefilled from whatever rules, target, location and ball type were used last, so starting a second match took noticeably fewer taps.
-
31 October 2025
CloudKit-backed sync and a settings diagnostics tab
SwiftData containers get real CloudKit mirroring, and sync problems become visible instead of silent.
Read more
After a quiet stretch, CloudKit backing was properly enabled for the SwiftData containers, with diagnostics tools added to a new settings tab to make sync problems visible rather than silent. That work surfaced a deeper issue: the app's notion of "the default player" was informal and prone to duplicating on reinstall — a problem that would take a proper identity store to fix, which came later as part of the v2 rewrite below.
-
26 April 2026 v2.0.0
v2.0.0 — the slot-based rewrite that became Rallying's foundation
A six-phase model rewrite fixes around twenty long-standing bugs, and its schema becomes the foundation the next app is built on.
Read more
A full schema redesign replaced the original model layer with slot-based player references, explicit game and match scoring detail, and a dedicated recordedBy field to distinguish who played from who scored — addressing roughly twenty latent bugs along the way, including an HR-chart crash, duplicate players on reinstall, and drawn games being conflated with incomplete ones. It shipped as v2.0.0, and that slot-based schema is what came next: rather than growing Squash Tracker sport-by-sport, the rewrite was lifted whole into a new, separately-provisioned multi-sport app called Rallying, alongside an export → import path for carrying existing match history across.
-
26 April 2026
Rallying is born — the v3.A port from Squash Tracker
A fresh multiplatform app, seeded with the proven schema, UI and CSV importer, gated to squash for launch.
Read more
Rallying started as a new Xcode multiplatform project, immediately followed by a straight port of the v3.A foundations from Squash Tracker: the slot-based schema, the shared iPhone UI (matches, players, locations, settings), the Watch recording surface, and the CSV importer that lets existing testers carry their history across. The sport picker was gated to the squash family for launch, so the app shipped as squash-only on day one even though the schema underneath was already multi-sport.
-
27 April 2026
v3.A.1 — past-match entry and a shared live-record controller
Matches played away from a device can be logged after the fact, and phone and watch share one recording brain.
Read more
A score cascade was added to the match create/edit flow so a match played without the app running could still be entered properly after the fact, rather than only ever recorded live. Alongside it, live recording was pulled out into a shared controller used by both the iPhone and Watch UI, so the two platforms record a match through the same logic rather than two parallel implementations drifting apart.
-
5 May 2026
A real app icon, and the first Internal TestFlight build
The placeholder icon is replaced with a layered design, and Rallying reaches its first testers.
Read more
The placeholder icon was replaced with a proper layered Rallying app icon, and the build tag was stamped into the release notes for the project's first Internal TestFlight build — the point Rallying stopped being a local port and became something an actual tester could install.
-
19 May 2026
CloudKit schema-as-code, and a guarded tokenless CI lint
The CloudKit schema becomes a checked-in file, with a lint that catches drift without needing a live token in CI.
Read more
CloudKit schema-as-code tooling was onboarded from the sibling apps: the live schema is exported to a checked-in file, validated locally, and checked in CI via a lint that is guarded to run without a CloudKit token — laying the sync foundation a multi-device, eventually multi-sport app depends on.