fix: 22 audit fixes — concurrency, memory, performance, accessibility

- Move 7 Data(contentsOf:) calls off MainActor via Task.detached (BootstrapService)
- Batch-fetch N+1 queries in sync merge loops (CanonicalSyncService)
- Predicate-based gamesForTeam fetch instead of fetching all games (DataProvider)
- Proper Sendable on RouteInfo with nonisolated(unsafe) polyline (LocationService)
- [weak self] in BGTaskScheduler register closures (BackgroundSyncManager)
- Cache tripDays, routeWaypoints as @State with recompute (TripDetailView)
- Remove unused AnyCancellable, add Task lifecycle management (TripDetailView)
- Cache sportStadiums, recentVisits as stored properties (ProgressViewModel)
- Dynamic Type fonts replacing hardcoded sizes (OnboardingPaywallView)
- Accessibility labels/hints on stadium picker, date picker, map, stats,
  settings toggle, and day cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-19 09:23:29 -06:00
parent dad3270be7
commit e7420061a5
12 changed files with 180 additions and 101 deletions

View File

@@ -202,12 +202,13 @@ final class BootstrapService {
throw BootstrapError.bundledResourceNotFound("stadiums_canonical.json")
}
let data: Data
let stadiums: [JSONCanonicalStadium]
do {
data = try Data(contentsOf: url)
stadiums = try JSONDecoder().decode([JSONCanonicalStadium].self, from: data)
stadiums = try await Task.detached {
let data = try Data(contentsOf: url)
return try JSONDecoder().decode([JSONCanonicalStadium].self, from: data)
}.value
} catch {
throw BootstrapError.jsonDecodingFailed("stadiums_canonical.json", error)
}
@@ -238,12 +239,13 @@ final class BootstrapService {
return
}
let data: Data
let aliases: [JSONStadiumAlias]
do {
data = try Data(contentsOf: url)
aliases = try JSONDecoder().decode([JSONStadiumAlias].self, from: data)
aliases = try await Task.detached {
let data = try Data(contentsOf: url)
return try JSONDecoder().decode([JSONStadiumAlias].self, from: data)
}.value
} catch {
throw BootstrapError.jsonDecodingFailed("stadium_aliases.json", error)
}
@@ -280,12 +282,13 @@ final class BootstrapService {
return
}
let data: Data
let structures: [JSONLeagueStructure]
do {
data = try Data(contentsOf: url)
structures = try JSONDecoder().decode([JSONLeagueStructure].self, from: data)
structures = try await Task.detached {
let data = try Data(contentsOf: url)
return try JSONDecoder().decode([JSONLeagueStructure].self, from: data)
}.value
} catch {
throw BootstrapError.jsonDecodingFailed("league_structure.json", error)
}
@@ -319,12 +322,13 @@ final class BootstrapService {
throw BootstrapError.bundledResourceNotFound("teams_canonical.json")
}
let data: Data
let teams: [JSONCanonicalTeam]
do {
data = try Data(contentsOf: url)
teams = try JSONDecoder().decode([JSONCanonicalTeam].self, from: data)
teams = try await Task.detached {
let data = try Data(contentsOf: url)
return try JSONDecoder().decode([JSONCanonicalTeam].self, from: data)
}.value
} catch {
throw BootstrapError.jsonDecodingFailed("teams_canonical.json", error)
}
@@ -352,12 +356,13 @@ final class BootstrapService {
return
}
let data: Data
let aliases: [JSONTeamAlias]
do {
data = try Data(contentsOf: url)
aliases = try JSONDecoder().decode([JSONTeamAlias].self, from: data)
aliases = try await Task.detached {
let data = try Data(contentsOf: url)
return try JSONDecoder().decode([JSONTeamAlias].self, from: data)
}.value
} catch {
throw BootstrapError.jsonDecodingFailed("team_aliases.json", error)
}
@@ -393,12 +398,13 @@ final class BootstrapService {
throw BootstrapError.bundledResourceNotFound("games_canonical.json")
}
let data: Data
let games: [JSONCanonicalGame]
do {
data = try Data(contentsOf: url)
games = try JSONDecoder().decode([JSONCanonicalGame].self, from: data)
games = try await Task.detached {
let data = try Data(contentsOf: url)
return try JSONDecoder().decode([JSONCanonicalGame].self, from: data)
}.value
} catch {
throw BootstrapError.jsonDecodingFailed("games_canonical.json", error)
}
@@ -462,12 +468,13 @@ final class BootstrapService {
return
}
let data: Data
let sports: [JSONCanonicalSport]
do {
data = try Data(contentsOf: url)
sports = try JSONDecoder().decode([JSONCanonicalSport].self, from: data)
sports = try await Task.detached {
let data = try Data(contentsOf: url)
return try JSONDecoder().decode([JSONCanonicalSport].self, from: data)
}.value
} catch {
throw BootstrapError.jsonDecodingFailed("sports_canonical.json", error)
}