A case study in end-to-end mobile systems design: SwiftUI architecture,
lifecycle orchestration, authenticated networking, resilient audio
workflows, and production-ready engineering.
Intro
Together with
Sioux, we built the Coach-Copilot app for
Equestic.
On the iOS side, I led the architectural direction and was responsible
for how the mobile system was designed and implemented. That included
defining the app structure, shaping shared state and navigation,
designing the networking and authentication layers, handling
lifecycle-aware behavior, and building the audio recording and upload
workflows so they could hold up under real-world mobile conditions.
What made the project especially interesting was that it required much
more than screen implementation. Coach-Copilot had to support
authenticated session flows, push and deep-link entry points,
lifecycle-heavy behavior, and durable audio workflows. From the mobile
perspective, that pushed the work firmly into systems design rather
than straightforward feature delivery.
Overview
Coach-Copilot is an AI-powered equestrian app delivered for
Equestic
through a collaboration between me as an external iOS contractor and
Sioux.
On the iOS side, I led the architectural direction and implemented the
foundations of the mobile system. The product supports live coaching
sessions, solo-ride reflections, and groundwork reflections, helping
coaches and riders capture training context and turn it into
structured, actionable follow-up.
The main engineering challenge was not building individual screens. It
was coordinating authentication, navigation, app lifecycle events,
audio recording, local persistence, background uploads, backend
activity state, and AI-assisted summaries under real mobile
conditions.
My Role
My responsibilities included:
- the layered SwiftUI application architecture
- shared observable state and dependency composition
- typed navigation and multi-entry flows
- activity-specific workflow orchestration
- authenticated networking and coordinated token refresh
- lifecycle, push-notification, and deep-link handling
- interruption-aware audio capture
- durable local recording and audio state
- persisted background upload workflows
- AI-assisted summary review and approval flows
- memberships, runtime environments, and remote configuration
- telemetry, crash reporting, and testable service boundaries
Architecture
The app uses a layered structure:
- SwiftUI views
- Observable view models and workflow state
- Feature services
- Repositories and stores
- HTTP execution, Keychain, UserDefaults, and file storage
Views focus on rendering and user interaction. View models and
workflow types handle presentation and multi-step orchestration.
Services coordinate feature behavior, while repositories and stores
isolate networking and persistence.
Dependencies are assembled at the application boundary, where the app
can select real or stub implementations. Shared state uses SwiftUI
Observation, while navigation is managed separately through typed
routes and an observable, main-actor-isolated router.
Unified Activity Workflows
The app models live coaching, solo rides, and groundwork reflections
through a shared activity workflow system.
Each activity defines its own:
- availability rules
- supported training specifications
- ordered setup steps
- completion behavior
- participant requirements
This reuses common infrastructure without pretending that every
activity has the same workflow. The activity lifecycle also provides
shared operations for cancellation, recording start, recording
finalization, and upload completion while routing each operation to
the correct backend implementation.
Authentication and Lifecycle Reliability
Authenticated networking uses typed endpoints, repositories, a shared
HTTP client, Keychain-backed token storage, and centralized refresh
coordination.
An actor-isolated token refresh coordinator prevents several
simultaneous 401 responses from starting duplicate
refresh requests. Waiting callers resume after one coordinated refresh
succeeds or receive the same terminal authentication failure if it
cannot recover.
Lifecycle behavior is centralized rather than distributed across
screens. It coordinates launch bootstrap, authentication revalidation,
foreground and background transitions, notifications, APNs token
synchronization, deep links, remote configuration, force-update
checks, recording policy, background-upload completion, and telemetry.
Audio as a Durable Workflow
Audio is treated as a subsystem rather than a single record-and-upload
action.
The recording layer handles:
- audio-session configuration
- input and Bluetooth route validation
- interruptions and route changes
- media-services resets
- elapsed-time and level monitoring
- background visual-update policy
- local file output and diagnostics
The newer local recording journal separates device-level recording
state from backend activity state. It models recording, local
finalization, finalized-asset creation, durable upload handoff,
backend acknowledgement, and recoverable failure as distinct stages.
Its segmented-recording design gives each segment a stable identity,
validated metadata, explicit state, and any known silence that must
precede it. Closed segments remain immutable, allowing recording to
continue safely in a new segment after interruptions or input-format
changes.
Raw segments remain owned by the local journal until a finalized audio
asset and a durable upload job have both been persisted. This prevents
audio from being deleted during the handoff between local export and
background upload ownership.
Background Uploads
Uploads are implemented as persisted, actor-isolated workflows with
three logical stages:
- notify the backend that recording was finalized
- transfer the audio file
- acknowledge completion of the backend workflow
Jobs support deduplication, retry with backoff, progress reporting,
cancellation, persistence, and relaunch recovery. A background
URLSession continues transfers independently of the
foreground UI and reconnects restored system tasks to their persisted
job identities.
This deliberately separates local recording durability,
finalized-asset durability, file-transfer completion, and
business-workflow completion.
Product and Production Systems
The same architecture also supports:
- editable and approvable AI-generated summaries
- referral, QR invitation, onboarding, and deep-link flows
- SaddleClip account linking
- Subscription-backed memberships and purchases
- Multiple environments
- Analytics and Remote Config
- Diagnostics and lifecycle breadcrumbs
- structured network, audio, and upload telemetry
- backend-independent development through stub services
The test target contains 29 test files and 22 mock, spy, stub, and
support files. Coverage spans repositories, services, shared state,
routing, system integration, and presentation logic.
Result
The result is a SwiftUI application designed as a coordinated
production system rather than a collection of screens.
Its key architectural outcomes are:
- extensible activity-specific workflows
-
explicit boundaries between UI, orchestration, services, and I/O
- coordinated authentication recovery
- centralized lifecycle and external-entry handling
- interruption-aware audio capture
- durable local recording identities and recovery state
- safe ownership transfer from recording to upload
- persisted, resumable background upload jobs
- separate transport and backend-completion states
- environment-aware composition, observability, and testability
Most importantly, the architecture supports the product’s real
purpose: reliably preserving training moments and transforming them
into structured information that coaches and riders can revisit and
act on.