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:
@@ -30,6 +30,7 @@ final class AchievementEngine {
|
||||
|
||||
private let modelContext: ModelContext
|
||||
private let dataProvider: AppDataProvider
|
||||
private var stadiumIdsBySport: [Sport: [String]]?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
@@ -356,6 +357,7 @@ final class AchievementEngine {
|
||||
case .visitsInDays(let requiredVisits, let days):
|
||||
// Find the qualifying window of visits
|
||||
let sortedVisits = visits.sorted { $0.visitDate < $1.visitDate }
|
||||
guard sortedVisits.count >= requiredVisits else { return [] }
|
||||
for i in 0...(sortedVisits.count - requiredVisits) {
|
||||
let windowStart = sortedVisits[i].visitDate
|
||||
let windowEnd = sortedVisits[i + requiredVisits - 1].visitDate
|
||||
@@ -399,15 +401,15 @@ final class AchievementEngine {
|
||||
}
|
||||
|
||||
private func getStadiumIdsForLeague(_ sport: Sport) -> [String] {
|
||||
// Get all stadium canonical IDs for this sport
|
||||
return dataProvider.stadiums
|
||||
.filter { stadium in
|
||||
// Check if stadium hosts teams of this sport
|
||||
dataProvider.teams.contains { team in
|
||||
team.stadiumId == stadium.id && team.sport == sport
|
||||
}
|
||||
// Build cache lazily on first access
|
||||
if stadiumIdsBySport == nil {
|
||||
var cache: [Sport: [String]] = [:]
|
||||
for team in dataProvider.teams {
|
||||
cache[team.sport, default: []].append(team.stadiumId)
|
||||
}
|
||||
.map { $0.id }
|
||||
stadiumIdsBySport = cache
|
||||
}
|
||||
return stadiumIdsBySport?[sport] ?? []
|
||||
}
|
||||
|
||||
// MARK: - Data Fetching
|
||||
|
||||
Reference in New Issue
Block a user