5511e07538684801e182a2d35a9717decadaa29b
Critical:
- ProgressViewModel: use single stored ModelContext instead of creating
new ones per operation (deleteVisit silently no-op'd)
- ProgressViewModel: convert expensive computed properties to stored
with explicit recompute after mutations (3x recomputation per render)
Memory:
- AnimatedSportsIcon: replace recursive GCD asyncAfter with Task loop,
cancelled in onDisappear (19 unkillable timer chains)
- ItineraryItemService: remove [weak self] from actor Task (semantically
wrong, silently drops flushPendingUpdates)
- VisitPhotoService: remove [weak self] from @MainActor Task closures
Concurrency:
- StoreManager: replace nested MainActor.run{Task{}} with direct await
in listenForTransactions (fire-and-forget race)
- VisitPhotoService: move JPEG encoding/file writing off MainActor via
nonisolated static helper + Task.detached
- SportsIconImageGenerator: replace GCD dispatch with Task.detached for
structured concurrency compliance
Performance:
- Game/RichGame: cache DateFormatters as static lets instead of
allocating per-call (hundreds of allocations in schedule view)
- TripDetailView: wrap ~10 routeWaypoints print() in #if DEBUG, remove
2 let _ = print() from TripMapView.body (fires every render)
Accessibility:
- GameRow: add combined VoiceOver label (was reading abbreviations
letter-by-letter)
- Sport badges: add accessibilityLabel to prevent SF symbol name readout
- SportsTimeApp: post UIAccessibility.screenChanged after bootstrap
completes so VoiceOver users know app is ready
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SportsTime
An iOS app for planning multi-stop sports road trips across North America. Plan your ultimate stadium-hopping adventure with intelligent route optimization, real-time game schedules, and detailed trip itineraries.
Features
- Multi-Sport Trip Planning - Plan trips across 7 professional leagues
- Three Planning Modes
- By Date Range - Find the best games within your travel window
- By Must-See Games - Build a trip around specific matchups you want to attend
- By Cities - Select departure/arrival cities and discover games along the route
- Intelligent Route Optimization - TSP solver optimizes multi-stop routes for minimum travel time
- Driving Constraints - Respects max daily driving hours and number of drivers
- Offline-First - Works without internet using locally cached schedule data
- PDF Export - Generate detailed trip itineraries with maps, stadium info, and nearby attractions
- Stadium Tracking - Track which stadiums you've visited across all leagues
Supported Leagues
| League | Sport | Season |
|---|---|---|
| MLB | Baseball | March - October |
| NBA | Basketball | October - June |
| NHL | Hockey | October - June |
| NFL | Football | September - February |
| MLS | Soccer | February - December |
| WNBA | Basketball | May - October |
| NWSL | Soccer | March - November |
Requirements
- iOS 26.0+
- Xcode 26.0+
- Swift 6.0+
Installation
-
Clone the repository:
git clone https://github.com/yourusername/SportsTime.git cd SportsTime -
Open the project in Xcode:
open SportsTime.xcodeproj -
Build and run on simulator or device.
Project Structure
SportsTime/
├── SportsTime/ # Main app target
│ ├── Core/ # Data layer
│ │ ├── Models/
│ │ │ ├── Domain/ # Pure Swift structs (Trip, Game, Stadium, Team)
│ │ │ ├── CloudKit/ # CKRecord wrappers
│ │ │ └── Local/ # SwiftData models
│ │ └── Services/ # CloudKit, Location services
│ ├── Planning/ # Domain layer - trip planning logic
│ │ └── Engine/ # TripPlanningEngine, RouteOptimizer, etc.
│ ├── Features/ # Presentation layer - SwiftUI views
│ │ ├── Home/
│ │ ├── Trip/
│ │ ├── Schedule/
│ │ ├── Progress/
│ │ └── Settings/
│ ├── Export/ # PDF generation
│ └── Resources/ # Bundled JSON data
├── SportsTimeTests/ # Unit tests
├── Scripts/ # Python data pipeline
│ └── sportstime_parser/ # Schedule scraping & CloudKit upload
├── data/ # Local data files
└── docs/ # Documentation
Architecture
Clean MVVM with feature-based modules and offline-first data architecture.
Three-Layer Architecture
- Presentation Layer (
Features/) - SwiftUI Views +@ObservableViewModels - Domain Layer (
Planning/) - Trip planning algorithms - Data Layer (
Core/) - Models and services
Data Flow
AppDataProvider.shared (Single Source of Truth)
│
├── Bundled JSON (bootstrap on first launch)
├── SwiftData (local persistence)
└── CloudKit (background sync)
All schedule data flows through AppDataProvider.shared - never access CloudKit or SwiftData directly for canonical data.
Development
Build
xcodebuild -project SportsTime.xcodeproj \
-scheme SportsTime \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' \
build
Run Tests
# All tests
xcodebuild -project SportsTime.xcodeproj \
-scheme SportsTime \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' \
test
# Specific test suite
xcodebuild -project SportsTime.xcodeproj \
-scheme SportsTime \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' \
-only-testing:SportsTimeTests/EdgeCaseTests \
test
Data Pipeline
The Python pipeline scrapes schedules from multiple sources and uploads to CloudKit:
cd Scripts
pip install -r requirements.txt
python -m sportstime_parser scrape --sport all --season 2026
python -m sportstime_parser upload --sport all
Test Suite
124 tests across 11 phases covering:
- Trip planning engine (all 3 scenarios)
- Route optimization (TSP solver)
- Travel estimation
- Game filtering and matching
- Edge cases and boundary conditions
- Concurrency behavior
Documentation
CLAUDE.md- Development guidelines and architecture detailsARCHITECTURE.md- Detailed system architecturedocs/TEST_PLAN.md- Test suite documentationdocs/MARKET_RESEARCH.md- Competitive analysis
License
MIT License - see LICENSE file for details.
Description
Languages
Cython
91.7%
Swift
8%
HTML
0.2%