DCC Weekly Activities: Building a 22K-Line iOS App End-to-End
- Mar 27
- 3 min read

A case study in building a production iOS app from scratch — native Swift, serverless backend, real-time data, and 55+ features delivered through disciplined Agile practices.
The Problem
The Desi Cycling Club needed a way for members to track weekly performance, compare stats, and stay motivated. Strava shows individual rides but has no club-level weekly leaderboards, coaching insights, or comparative analytics. Members wanted to see who rode the most, track trends week-over-week, and get personalised coaching tips — all without requiring every member to share their individual Strava credentials.
The Architecture Decision
I chose native Swift/SwiftUI over cross-platform frameworks (React Native, Flutter) for three reasons: platform-native performance, direct access to Face ID and Speech Recognition APIs, and zero third-party UI dependencies. For the backend, Cloudflare Workers won over Firebase and AWS — edge-deployed, sub-100ms cached responses, generous free tier, and deploys in under 10 seconds via Wrangler CLI.
The key architectural choice was club code authentication — a shared code (DCC2026) instead of individual Strava OAuth. This keeps the app simple for members while a single bot account on the Worker fetches club data server-side. Strava secrets never touch the client binary.

Tech Stack
Swift 6 / SwiftUI — async/await concurrency, @Observable pattern, iOS 17+
Cloudflare Workers (JavaScript) — serverless API with KV edge caching and cron triggers
Strava API v3 — OAuth token proxy, club activity aggregation, sport type enrichment
Jira REST API — automated issue creation from in-app voice/text feature requests
Face ID / Touch ID — biometric authentication with iOS Keychain storage
Speech Recognition — on-device voice dictation (no cloud transcription)
Swift Charts — radar charts, scatter plots, distance bar charts
Scale & Complexity
80 Swift files, ~22,000 lines of production code
55+ features shipped across 12 categories, all tracked in Jira
5 interactive tabs: Ride, Overview, Leaderboard, Insights, Analysis
iPad adaptive layout with sidebar navigation and Stage Manager support
Apple TV companion app (read-only club dashboard)
Protected Core Framework (SCRUM-56) — 3-tier file protection with 6-step change control
Compile-time Feature Registry — build fails if any critical type is accidentally removed
Feature Highlights
Weekly report table with trend arrows and week-on-week comparison
Interactive charts: 5-axis radar, speed vs elevation scatter, distance bars
AI coaching tips with What-If scenarios and gap analysis
Animated performance rings showing your stats vs the club
Ride type filter — Road, Mountain Bike, Gravel, E-Bike, Virtual
Voice dictation feature requests that create Jira tickets automatically
Offline support with disk caching and queued requests that auto-send
Celebration cards with confetti animations for every rider
Live Feature Dashboard
The interactive feature dashboard below is live — it pulls directly from the Jira SCRUM project via a Cloudflare Worker, auto-categorises features by status (Live, Planned, Shelved), and refreshes hourly. Search, filter, and explore all 55+ features:
Explore the live dashboard: DCC Feature Dashboard
What I Learned
The biggest lesson was that discipline scales. The Protected Core Framework — inspired by change control processes I learned in investment banking — prevented every potential regression across 55+ feature additions. The 6-step process (Impact Declaration, Audit, Proposal, Approval Gate, Implementation, Documentation) feels heavy upfront but pays for itself the first time it catches a breaking change.
The second lesson: AI-assisted development is transformative when given guardrails. A CLAUDE.md file checked into the repo encodes architecture rules, protected files, and coding standards. The AI reads it every session, producing consistent, high-quality code without accidentally restructuring critical flows. I wrote about this pattern in a separate post.
If I started over, I would modularise the Cloudflare Worker into separate files for testability, and add Durable Objects for real-time WebSocket push. But for a club-sized app, the single-file Worker approach is refreshingly simple — and it ships.










Comments