Covers performance (launch freeze, list lag), UI polish (animations, missing location info, timezone display), and scraper alias fix. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
9.6 KiB
TODO Bugs Design Document
Date: 2026-01-12 Status: Ready for Implementation
Overview
This document outlines fixes for 9 bugs from TO-DOS.md affecting performance, UI polish, and the data scraper.
Category A: Performance Issues (3 bugs)
Bug 1: UI Unresponsive on Launch Until Featured Loads
Problem: App freezes on launch while generating suggested trips.
Root Cause: SuggestedTripsGenerator is @MainActor, so all TripPlanningEngine work (including TSP solving) blocks the main thread.
Files to Modify:
SportsTime/Core/Services/SuggestedTripsGenerator.swiftSportsTime/Features/Home/Views/HomeView.swift
Fix:
- Move heavy computation to background using
Task.detached - Show skeleton/placeholder UI immediately
- Keep only UI state updates on
@MainActor
Implementation:
func generateTrips() async {
guard !isLoading else { return }
isLoading = true
loadingMessage = await loadingTextGenerator.generateMessage()
// Move heavy work off main actor
let result = await Task.detached(priority: .userInitiated) { [dataProvider, planningEngine] in
// Ensure data is loaded
let teams = await dataProvider.teams
let stadiums = await dataProvider.stadiums
let games = try? await dataProvider.filterGames(...)
// All planning engine work here
// Return generated trips
}.value
// Back on MainActor for UI update
self.suggestedTrips = result
isLoading = false
}
Bug 2: Lag on Big Data Sets ("By Game")
Problem: Schedule list view with 1000+ games causes UI lag.
Root Cause: SwiftUI List struggles with large datasets. Each GameRowView performs:
- Rich game lookups
- Date formatting on every render
- No virtualization
Files to Modify:
SportsTime/Features/Schedule/Views/ScheduleListView.swiftSportsTime/Features/Schedule/ViewModels/ScheduleViewModel.swift
Fix:
- Implement pagination with visible window
- Cache formatted dates and lookups
- Use
LazyVStackwith explicit.id()for efficient diffing
Implementation:
struct ScheduleListView: View {
@State private var visibleRange: Range<Int> = 0..<50
private let pageSize = 50
var body: some View {
ScrollView {
LazyVStack(spacing: 0) {
ForEach(visibleGames) { game in
GameRowView(game: game, showDate: true)
.id(game.id)
.onAppear {
loadMoreIfNeeded(game)
}
}
}
}
}
private func loadMoreIfNeeded(_ game: RichGame) {
// Load next page when approaching end
}
}
Bug 4: Select Games View Laggy and Two Different Colors
Problem: Game selection view has performance issues and visual inconsistency.
Root Cause: Same as Bug 2 - large list without virtualization. Color issue likely from inconsistent background modifiers.
Files to Modify:
SportsTime/Features/Trip/Views/TripCreationView.swift(game selection section)
Fix:
- Apply same pagination fix as Bug 2
- Audit background colors - use consistent
.background()modifier - Ensure selected/unselected states use same base colors
Category B: Missing Info & Animation Issues (3 bugs)
Bug 3: "By Games" Missing Location Info
Problem: Games grouped by date don't show stadium/city.
Root Cause: GameRowView designed for team-grouped context where location is implicit.
Files to Modify:
SportsTime/Features/Schedule/Views/GameRowView.swiftSportsTime/Features/Schedule/Views/ScheduleListView.swift
Fix:
- Add
showLocation: Boolparameter toGameRowView - Pass
truewhengroupBy == .byGame
Implementation:
struct GameRowView: View {
let game: RichGame
var showDate: Bool = false
var showLocation: Bool = false // New parameter
var body: some View {
VStack(alignment: .leading, spacing: 4) {
// Existing matchup content...
if showLocation {
Text("\(game.stadium.name), \(game.stadium.city)")
.font(.caption)
.foregroundStyle(.secondary)
}
}
}
}
// In ScheduleListView:
GameRowView(game: game, showDate: true, showLocation: groupBy == .byGame)
Bug 5: Weird Animation When Selecting a Game
Problem: Unexpected animation when tapping a game in selection view.
Root Cause: SwiftUI animation conflict between List selection and state update.
Files to Modify:
SportsTime/Features/Trip/Views/TripCreationView.swift
Fix:
- Wrap selection in transaction to disable implicit animation
- Or use
.animation(nil, value:)to suppress
Implementation:
// Option A: Disable animation on selection
Button {
var transaction = Transaction()
transaction.disablesAnimations = true
withTransaction(transaction) {
viewModel.toggleGameSelection(game)
}
} label: {
GameSelectionRow(game: game, isSelected: isSelected)
}
.buttonStyle(.plain)
// Option B: On the selection indicator
Image(systemName: isSelected ? "checkmark.circle.fill" : "circle")
.animation(nil, value: isSelected)
Bug 6: Trip Game Times Should Be UTC to Local
Problem: Game times display in device timezone, not stadium local time.
Root Cause: DateFormatter without explicit timezone uses device default.
Files to Modify:
SportsTime/Core/Models/Domain/Stadium.swiftSportsTime/Core/Models/Domain/Game.swift
Fix:
- Add
timeZoneIdentifier: Stringto Stadium (or derive from coordinates) - Create
localGameTimecomputed property onRichGame - Display with timezone abbreviation: "7:05 PM ET"
Implementation:
// Stadium.swift - add timezone
struct Stadium: Identifiable, Codable, Hashable {
// ... existing properties
let timeZoneIdentifier: String? // e.g., "America/New_York"
var timeZone: TimeZone? {
timeZoneIdentifier.flatMap { TimeZone(identifier: $0) }
}
}
// RichGame extension
extension RichGame {
var localGameTime: String {
let formatter = DateFormatter()
formatter.dateFormat = "h:mm a z"
formatter.timeZone = stadium.timeZone ?? .current
return formatter.string(from: game.dateTime)
}
}
Note: Requires populating timeZoneIdentifier for all stadiums in:
- Bundled JSON bootstrap data
- CloudKit stadium records
- Scraper stadium resolver
Category C: UI Polish Issues (2 bugs)
Bug 7: Pace Capsule Animation Looks Off
Problem: "packed"/"moderate"/"relaxed" label has glitchy animation.
Root Cause: Implicit animation conflict or text morphing transition.
Files to Modify:
SportsTime/Features/Home/Views/SavedTripsListView.swift(or wherever pace capsule is)
Fix:
- Use
.contentTransition(.identity)to prevent text morphing - Disable animation on the capsule when trip data changes
Implementation:
Text(trip.paceLabel)
.font(.caption2)
.fontWeight(.medium)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(paceColor.opacity(0.2))
.foregroundStyle(paceColor)
.clipShape(Capsule())
.contentTransition(.identity)
.animation(nil, value: trip.id)
Bug 8: Remove "My Trips" Title from Tab View
Problem: Redundant title appearing in the My Trips tab.
Files to Modify:
SportsTime/Features/Home/Views/HomeView.swift(or main TabView)
Fix:
- Remove
.navigationTitle()from SavedTripsListView root - Or hide navigation bar if title is inherited
Implementation:
// In TabView:
NavigationStack {
SavedTripsListView(trips: savedTrips)
.toolbar(.hidden, for: .navigationBar)
// Or just remove any .navigationTitle("My Trips")
}
.tabItem {
Label("My Trips", systemImage: "suitcase")
}
Category D: Scraper Issue (1 bug)
Bug 9: Frost Bank Center Team Mapping Missing
Problem: NBA scraper can't resolve Frost Bank Center (Spurs home).
Root Cause: Scraper likely returns old name "AT&T Center" which doesn't match. Stadium exists in resolver but alias is missing.
Files to Modify:
Scripts/stadium_aliases.json
Fix:
- Add alias for "AT&T Center" → "stadium_nba_frost_bank_center"
- Check scraper logs for exact unresolved name
Implementation:
{
"canonical_id": "stadium_nba_frost_bank_center",
"alias_name": "AT&T Center",
"sport": "nba",
"valid_from": "2002-10-18",
"valid_to": "2024-07-01",
"notes": "Renamed to Frost Bank Center in 2024"
}
Investigation: Run NBA scraper with verbose logging to confirm the exact stadium name being returned.
Implementation Priority
-
High Impact (Performance):
- Bug 1: Launch responsiveness (blocking UX)
- Bug 2 & 4: List performance (core feature usability)
-
Medium Impact (UX Polish):
- Bug 3: Location info (missing data)
- Bug 6: Timezone display (incorrect data)
- Bug 5: Selection animation (visual glitch)
-
Low Impact (Minor Polish):
- Bug 7: Pace capsule animation
- Bug 8: Remove redundant title
- Bug 9: Scraper alias (data pipeline)
Testing Checklist
- App launches with responsive UI, trips load in background
- "By Game" view scrolls smoothly with 1000+ games
- "By Game" shows stadium and city for each game
- Game selection has no weird animation on tap
- Trip game times show stadium local time with timezone (e.g., "7:05 PM ET")
- Pace capsule has no animation glitch
- "My Trips" tab has no redundant title
- NBA scraper resolves Spurs games to Frost Bank Center