- Document Export layer (PDFGenerator, asset services) - Add iOS 26 API notes (deprecated CLGeocoder, Swift 6 patterns) - Add Documentation section pointing to docs/ - Expand Future Phases with market research findings (bucket list, group coordination, fan community) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
8.9 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Build & Run Commands
# Build the iOS app
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' build
# Run tests
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' test
# Run specific test suite
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' -only-testing:SportsTimeTests/TripPlanningEngineTests test
# Run a single test
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' -only-testing:SportsTimeTests/TestClassName/testMethodName test
# Data scraping (Python)
cd Scripts && pip install -r requirements.txt
python scrape_schedules.py --sport all --season 2026
Architecture Overview
This is an iOS app for planning multi-stop sports road trips. It uses Clean MVVM with feature-based modules.
Three-Layer Architecture
-
Presentation Layer (
Features/): SwiftUI Views + @Observable ViewModels organized by feature (Home, Trip, Schedule, Settings) -
Domain Layer (
Planning/): Trip planning logicTripPlanningEngine- Main orchestrator, 7-step algorithmRouteOptimizer- TSP solver (exact for <8 stops, heuristic otherwise)ScheduleMatcher- Finds games along route corridorTripScorer- Multi-factor scoring (game quality, route efficiency, leisure balance)
-
Data Layer (
Core/):Models/Domain/- Pure Swift structs (Trip, Game, Stadium, Team)Models/CloudKit/- CKRecord wrappers for public databaseModels/Local/- SwiftData models for local persistence (SavedTrip, UserPreferences)Services/- CloudKitService (schedules), LocationService (geocoding/routing)
-
Export Layer (
Export/):PDFGenerator- Generates PDF trip itineraries with maps, photos, and attractionsExportService- Orchestrates PDF export with asset prefetchingServices/MapSnapshotService- Generates static map images via MKMapSnapshotterServices/RemoteImageService- Downloads/caches team logos and stadium photosServices/POISearchService- Finds nearby restaurants, attractions via MKLocalSearchServices/PDFAssetPrefetcher- Parallel prefetching of all PDF assets
Data Storage Strategy
- CloudKit Public DB: Read-only schedules, stadiums, teams (shared across all users)
- SwiftData Local: User's saved trips, preferences, cached schedules
- No network dependency for trip planning once schedules are synced
Key Data Flow
TripCreationView → TripCreationViewModel → PlanningRequest
→ TripPlanningEngine (ScheduleMatcher + RouteOptimizer + TripScorer)
→ PlanningResult → Trip → TripDetailView → SavedTrip (persist)
Important Patterns
- ViewModels use
@Observable(not ObservableObject) - All planning engine components are
actortypes for thread safety - Domain models are pure Codable structs; SwiftData models wrap them via encoded
Datafields - CloudKit container ID:
iCloud.com.sportstime.app PDFGeneratorandExportServiceare@MainActor final class(not actors) because they access MainActor-isolated UI properties and use UIKit drawing
iOS 26 API Notes
Deprecated APIs (use with @available(iOS, deprecated: 26.0) annotation):
CLGeocoder→ UseMKLocalSearchwith.addressresult type insteadMKPlacemarkproperties (locality, administrativeArea, etc.) → Still work but deprecated; useMKMapItemproperties where possibleMKMapItem.locationis non-optional in iOS 26 (returnsCLLocation, notCLLocation?)
Swift 6 Concurrency:
- Use
@retroactivefor protocol conformances on types you don't own (e.g.,CLLocationCoordinate2D: @retroactive Codable) - When capturing
varinasync let, create immutable copies first to avoid Swift 6 warnings
Key View Components
TripDetailView (Features/Trip/Views/TripDetailView.swift)
Displays trip itinerary with conflict detection for same-day games in different cities.
Conflict Detection System:
detectConflicts(for: ItineraryDay)- Checks if multiple stops have games on the same calendar day- Returns
DayConflictInfowithhasConflict,conflictingStops, andconflictingCities
RouteOptionsCard (Expandable):
- Shows when multiple route options exist for the same day (conflicting games in different cities)
- Collapsed: Shows "N route options" with city list, tap to expand
- Expanded: Shows each option as a
RouteOptionCardwith numbered badge (Option 1, Option 2, etc.) - Single routes (no conflict): Uses regular
DayCard, auto-expanded
RouteOptionCard:
- Individual option within the expandable RouteOptionsCard
- Shows option number badge, city name, games at that stop, and travel info
DayCard Component (non-conflict mode):
specificStop: TripStop?- When provided, shows only that stop's gamesprimaryCityForDay- Returns the city for the cardgamesOnThisDay- Returns games filtered to the calendar day
Visual Design:
- Expandable cards have orange border and branch icon
- Option badges are blue capsules
- Chevron indicates expand/collapse state
Scripts
Scripts/scrape_schedules.py scrapes NBA/MLB/NHL schedules from multiple sources (Basketball-Reference, Baseball-Reference, Hockey-Reference, official APIs) for cross-validation. See Scripts/DATA_SOURCES.md for source URLs and rate limits.
Documentation
The docs/ directory contains project documentation:
MARKET_RESEARCH.md- Competitive analysis and feature recommendations based on sports travel app market research (January 2026)
Test Suites
- TripPlanningEngineTests (50 tests) - Routing logic, must-see games, required destinations, EV charging, edge cases
- DayCardTests (11 tests) - DayCard conflict detection, warning display, stop filtering, edge cases
- DuplicateGameIdTests (2 tests) - Regression tests for handling duplicate game IDs in JSON data
Bug Fix Protocol
Whenever fixing a bug:
- Write a regression test that reproduces the bug before fixing it
- Include edge cases - test boundary conditions, null/empty inputs, and related scenarios
- Confirm all tests pass by running the test suite before considering the fix complete
- Name tests descriptively - e.g.,
test_DayCard_OnlyShowsGamesFromPrimaryStop_WhenMultipleStopsOverlapSameDay
Example workflow:
# 1. Write failing test that reproduces the bug
# 2. Fix the bug
# 3. Verify the new test passes along with all existing tests
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' test
Future Phases
See docs/MARKET_RESEARCH.md for full competitive analysis and feature prioritization.
Phase 2: AI-Powered Trip Planning
Natural Language Trip Planning
- Allow users to describe trips in plain English: "plan me a baseball trip from Texas" or "I want to see the Yankees and Red Sox in one weekend"
- Parse intent, extract constraints (sports, dates, locations, budget)
- Generate trip suggestions from natural language input
On-Device Intelligence (Apple Foundation Models)
- Use Apple's Foundation Models framework (iOS 26+) for on-device AI processing
- Privacy-preserving - no data leaves the device
- Features to enable:
- Smart trip suggestions based on user history
- Natural language query understanding
- Personalized game recommendations
- Conversational trip refinement ("add another game" / "make it shorter")
Implementation Notes:
- Foundation Models requires iOS 26+ and Apple Silicon
- Use
@Generablefor structured output parsing - Implement graceful fallback for unsupported devices
- See
axiom:axiom-foundation-modelsskill for patterns
Phase 3: Stadium Bucket List
Progress Tracking
- Visual map showing visited vs. remaining stadiums per league
- Digital passport/stamps for each visited stadium
- Achievement badges (e.g., "All NL West", "Coast to Coast", "10 Stadiums")
- Shareable progress cards for social media
Competitors: Baseball Bucket List, Sports Venue Tracker, MLB BallPark Pass-Port (physical)
Phase 4: Group Trip Coordination
Collaborative Planning
- Invite friends to collaborate on trip planning
- Polling/voting on game choices and destinations
- Expense splitting integration
- Shared itinerary with real-time sync
- Role delegation (lodging, tickets, restaurants)
Competitors: SquadTrip, Troupe, Howbout
Phase 5: Fan Community
Social Features
- Stadium tips from locals (best food, parking, pre-game bars)
- Fan meetup coordination for away games
- Trip reviews and ratings
- Discussion forums for specific stadiums
Competitor: Fantrip (fan-to-fan stays and local tips)
User Instruction
Do not commit code without prompting the user first.