Your performance is art. Stride is the brush.
Transforms Strava API run data into premium, share-ready graphics: choose a run, add data-driven stickers, overlay on a photo, and export to Instagram or your camera roll.
SwiftUI @Observable MapKit Strava API Interactive Photo Editor Gestures & Stickers async/await Keychain iOS 26+
demo.mov
- OAuth sign-in — no separate account
- Automatic run feed with infinite scroll
- Real data: distance, pace, time, location, route maps
- Full-screen zoomable, pannable canvas
- Pinch to zoom, drag to reposition
- Stickers can be dragged freely — including off-edge for creative compositions
- Export matches what you see, pixel for pixel
25 distinct sticker layouts across 7 categories:
- Big Metric — Hero numbers with supporting details
- Bars & Strips — Compact horizontal performance strips
- Badges — Rounded, centered compositions with labels
- Editorial — Structured label-over-value hierarchy
- Compositions — Asymmetric layouts with visual tension
- Minimal & Special — Clean single-metric and location stickers
- PR Celebration — Six dedicated layouts for personal records
Every sticker is dynamically populated from the selected run.
- Instagram Stories — One tap. Canvas exports to pasteboard, Instagram opens with the image as your story background.
- Save — Export to photo library at display scale
- Share — System share sheet for any destination
- Dark theme with strong orange accent
- Centralized color, typography, spacing, and corner radius tokens
- Four reusable button styles: Accent, Accent Outline, Ghost, Floating Circle
- Custom display fonts: Humane Bold, ROUND8-FOUR
View-centric SwiftUI with @Observable services. Views own transient state. Observable classes manage shared and persistent state. No third-party dependencies.
| Layer | Mechanism |
|---|---|
| Auth & session | @Observable StravaSession, injected via .environment() |
| Feed | @Observable RunFeedViewModel with pagination |
| Photo library | @Observable PhotoLibraryService, local to picker |
| Canvas | @State arrays and bindings, local to editor |
- Raw
URLSessionwithasync/await - Static API clients (
StravaAuthService,StravaAPIClient) - OAuth2 with automatic token refresh via Keychain
ASWebAuthenticationSessionfor OAuth login- Secure token storage in Keychain (
StravaTokenStore) - Activity feed with polyline decoding for MapKit route rendering
- In-memory map snapshot cache to prevent scroll jitter
RunFeedItem
→ .stickerData (immutable snapshot)
→ StickerLayoutType.allCases.filter(isAvailable)
→ StickerLayoutRouter (switch → view)
→ StickerOverlayView (drag, pinch, animate)
→ StickerDrawingView (export)
Adding a new sticker: add a case to the enum, create the view, register in the router. The picker, canvas, and export pipeline adapt automatically.
All visual constants live in AppTheme.swift:
AppColors— backgrounds, text, accent, utilityAppFont— metric, header, body, metadata, buttonAppSpacing— xs through xxlAppRadius— sm, md, lg, card
Stride/
├── Strava/ Auth, API client, token store, models
├── Feed/ Run feed, cards, map snapshots, polyline decoder
├── Profile/ Athlete display
├── Login/ Strava OAuth screen
├── BackgroundSelection/ Photo picker, editor, canvas, export
│ └── StickerLayouts/ 25 layout views, data model, router, PR system
├── Theme/ Design system
└── Resources/Fonts/ Humane-Bold, ROUND8-FOUR
- iOS 26+
- Strava account
- Photo library access