Files
Sportstime/CLAUDE.md
Trey t 2281440bf8 Update CLAUDE.md with export layer docs and future roadmap
- 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>
2026-01-08 13:30:49 -06:00

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

  1. Presentation Layer (Features/): SwiftUI Views + @Observable ViewModels organized by feature (Home, Trip, Schedule, Settings)

  2. Domain Layer (Planning/): Trip planning logic

    • TripPlanningEngine - Main orchestrator, 7-step algorithm
    • RouteOptimizer - TSP solver (exact for <8 stops, heuristic otherwise)
    • ScheduleMatcher - Finds games along route corridor
    • TripScorer - Multi-factor scoring (game quality, route efficiency, leisure balance)
  3. Data Layer (Core/):

    • Models/Domain/ - Pure Swift structs (Trip, Game, Stadium, Team)
    • Models/CloudKit/ - CKRecord wrappers for public database
    • Models/Local/ - SwiftData models for local persistence (SavedTrip, UserPreferences)
    • Services/ - CloudKitService (schedules), LocationService (geocoding/routing)
  4. Export Layer (Export/):

    • PDFGenerator - Generates PDF trip itineraries with maps, photos, and attractions
    • ExportService - Orchestrates PDF export with asset prefetching
    • Services/MapSnapshotService - Generates static map images via MKMapSnapshotter
    • Services/RemoteImageService - Downloads/caches team logos and stadium photos
    • Services/POISearchService - Finds nearby restaurants, attractions via MKLocalSearch
    • Services/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 actor types for thread safety
  • Domain models are pure Codable structs; SwiftData models wrap them via encoded Data fields
  • CloudKit container ID: iCloud.com.sportstime.app
  • PDFGenerator and ExportService are @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 → Use MKLocalSearch with .address result type instead
  • MKPlacemark properties (locality, administrativeArea, etc.) → Still work but deprecated; use MKMapItem properties where possible
  • MKMapItem.location is non-optional in iOS 26 (returns CLLocation, not CLLocation?)

Swift 6 Concurrency:

  • Use @retroactive for protocol conformances on types you don't own (e.g., CLLocationCoordinate2D: @retroactive Codable)
  • When capturing var in async 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 DayConflictInfo with hasConflict, conflictingStops, and conflictingCities

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 RouteOptionCard with 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 games
  • primaryCityForDay - Returns the city for the card
  • gamesOnThisDay - 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:

  1. Write a regression test that reproduces the bug before fixing it
  2. Include edge cases - test boundary conditions, null/empty inputs, and related scenarios
  3. Confirm all tests pass by running the test suite before considering the fix complete
  4. 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 @Generable for structured output parsing
  • Implement graceful fallback for unsupported devices
  • See axiom:axiom-foundation-models skill 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.