fix: codebase audit fixes — safety, accessibility, and production hygiene

Address 16 issues from external audit:
- Move StoreKit transaction listener ownership to StoreManager singleton with proper deinit
- Remove noisy VoiceOver announcements, add missing accessibility on StatPill and BootstrapLoadingView
- Replace String @retroactive Identifiable with IdentifiableShareCode wrapper
- Add crash guard in AchievementEngine getContributingVisitIds + cache stadium lookups
- Pre-compute GamesHistoryViewModel filtered properties to avoid redundant SwiftUI recomputation
- Remove force-unwraps in ProgressMapView with safe guard-let fallback
- Add diff-based update gating in ItineraryTableViewWrapper to prevent unnecessary reloads
- Replace deprecated UIScreen.main with UIWindowScene lookup
- Add deinit task cancellation in ScheduleViewModel and SuggestedTripsGenerator
- Wrap ~234 unguarded print() calls across 27 files in #if DEBUG

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-22 00:07:53 -06:00
parent 826eadbc0f
commit 91c5eac22d
32 changed files with 434 additions and 67 deletions

View File

@@ -293,6 +293,7 @@ final class GameMatcher {
metadata: PhotoMetadata,
sport: Sport? = nil
) async -> PhotoImportCandidate {
#if DEBUG
print("🎯 [GameMatcher] Processing photo for import")
print("🎯 [GameMatcher] - hasValidDate: \(metadata.hasValidDate)")
print("🎯 [GameMatcher] - captureDate: \(metadata.captureDate?.description ?? "nil")")
@@ -300,6 +301,7 @@ final class GameMatcher {
if let coords = metadata.coordinates {
print("🎯 [GameMatcher] - coordinates: \(coords.latitude), \(coords.longitude)")
}
#endif
// Get stadium matches regardless of game matching
var stadiumMatches: [StadiumMatch] = []
@@ -308,16 +310,21 @@ final class GameMatcher {
coordinates: coordinates,
sport: sport
)
#if DEBUG
print("🎯 [GameMatcher] - nearby stadiums found: \(stadiumMatches.count)")
for match in stadiumMatches.prefix(3) {
print("🎯 [GameMatcher] • \(match.stadium.name) (\(String(format: "%.1f", match.distance / 1609.34)) mi)")
}
#endif
} else {
#if DEBUG
print("🎯 [GameMatcher] - no coordinates, skipping stadium proximity search")
#endif
}
var matchResult = await matchGame(metadata: metadata, sport: sport)
#if DEBUG
switch matchResult {
case .singleMatch(let match):
print("🎯 [GameMatcher] - result: singleMatch - \(match.homeTeam.fullName) vs \(match.awayTeam.fullName)")
@@ -325,21 +332,30 @@ final class GameMatcher {
print("🎯 [GameMatcher] - result: multipleMatches (\(matches.count) games)")
case .noMatches(let reason):
print("🎯 [GameMatcher] - result: noMatches - \(reason)")
}
#endif
if case .noMatches = matchResult {
// FALLBACK: Try scraping historical game data if we have stadium + date
// Try all nearby stadiums (important for multi-sport venues like American Airlines Center)
if let captureDate = metadata.captureDate, !stadiumMatches.isEmpty {
#if DEBUG
print("🎯 [GameMatcher] - Trying historical scraper fallback...")
#endif
for stadiumMatch in stadiumMatches {
let stadium = stadiumMatch.stadium
#if DEBUG
print("🎯 [GameMatcher] - Trying \(stadium.name) (\(stadium.sport.rawValue))...")
#endif
if let scrapedGame = await HistoricalGameScraper.shared.scrapeGame(
stadium: stadium,
date: captureDate
) {
#if DEBUG
print("🎯 [GameMatcher] - ✅ Scraper found: \(scrapedGame.awayTeam) @ \(scrapedGame.homeTeam)")
#endif
// Convert ScrapedGame to a match result
matchResult = .singleMatch(GameMatchCandidate(
@@ -350,9 +366,11 @@ final class GameMatcher {
}
}
#if DEBUG
if case .noMatches = matchResult {
print("🎯 [GameMatcher] - Scraper found no game at any stadium")
}
#endif
}
}