Stabilize beta release with warning cleanup and edge-case fixes

This commit is contained in:
Trey t
2026-02-22 13:18:14 -06:00
parent fddea81e36
commit ec2bbb4764
55 changed files with 712 additions and 315 deletions

View File

@@ -16,8 +16,8 @@ final class ScheduleViewModel {
// MARK: - Filter State
var selectedSports: Set<Sport> = Set(Sport.supported)
var startDate: Date = Calendar.current.startOfDay(for: Date())
var endDate: Date = Calendar.current.date(byAdding: .day, value: 14, to: Calendar.current.startOfDay(for: Date())) ?? Date()
var startDate: Date = ScheduleViewModel.defaultDateRange().start
var endDate: Date = ScheduleViewModel.defaultDateRange().end
var searchText: String = ""
// MARK: - Data State
@@ -28,7 +28,9 @@ final class ScheduleViewModel {
private(set) var errorMessage: String?
private let dataProvider = AppDataProvider.shared
@ObservationIgnored
nonisolated(unsafe) private var loadTask: Task<Void, Never>?
private var latestLoadRequestID = UUID()
// MARK: - Pre-computed Groupings (avoid computed property overhead)
@@ -48,14 +50,43 @@ final class ScheduleViewModel {
}
var hasFilters: Bool {
selectedSports.count < Sport.supported.count || !searchText.isEmpty
let defaults = Self.defaultDateRange()
let calendar = Calendar.current
let hasDateFilter = !calendar.isDate(startDate, inSameDayAs: defaults.start) ||
!calendar.isDate(endDate, inSameDayAs: defaults.end)
return selectedSports.count < Sport.supported.count || !searchText.isEmpty || hasDateFilter
}
private static func defaultDateRange() -> (start: Date, end: Date) {
let calendar = Calendar.current
let start = calendar.startOfDay(for: Date())
let endDay = calendar.date(byAdding: .day, value: 14, to: start) ?? start
let end = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: endDay) ?? endDay
return (start, end)
}
private static func normalizedDateRange(start: Date, end: Date) -> (start: Date, end: Date) {
let calendar = Calendar.current
let normalizedStart = calendar.startOfDay(for: start)
let endDay = calendar.startOfDay(for: end)
let normalizedEnd = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: endDay) ?? endDay
return (normalizedStart, normalizedEnd)
}
// MARK: - Actions
func loadGames() async {
guard !selectedSports.isEmpty else {
let requestID = beginLoadRequest()
let queryStartDate = startDate
let queryEndDate = endDate
let querySports = selectedSports
guard !querySports.isEmpty else {
guard !isStaleLoad(requestID) else { return }
games = []
isLoading = false
error = nil
errorMessage = nil
updateFilteredGames()
return
}
@@ -64,11 +95,6 @@ final class ScheduleViewModel {
error = nil
errorMessage = nil
// Start diagnostics
let queryStartDate = startDate
let queryEndDate = endDate
let querySports = selectedSports
logger.info("📅 Loading games: \(querySports.map(\.rawValue).joined(separator: ", "))")
logger.info("📅 Date range: \(queryStartDate.formatted()) to \(queryEndDate.formatted())")
@@ -78,9 +104,11 @@ final class ScheduleViewModel {
logger.info("📅 Teams empty, loading initial data...")
await dataProvider.loadInitialData()
}
guard !isStaleLoad(requestID) else { return }
// Check if data provider had an error
if let providerError = dataProvider.errorMessage {
guard !isStaleLoad(requestID) else { return }
self.errorMessage = providerError
self.error = dataProvider.error
isLoading = false
@@ -92,11 +120,13 @@ final class ScheduleViewModel {
// Log team/stadium counts for diagnostics
logger.info("📅 Loaded \(self.dataProvider.teams.count) teams, \(self.dataProvider.stadiums.count) stadiums")
games = try await dataProvider.filterRichGames(
sports: selectedSports,
startDate: startDate,
endDate: endDate
let loadedGames = try await dataProvider.filterRichGames(
sports: querySports,
startDate: queryStartDate,
endDate: queryEndDate
)
guard !isStaleLoad(requestID) else { return }
games = loadedGames
// Update diagnostics
var newDiagnostics = ScheduleDiagnostics()
@@ -116,7 +146,7 @@ final class ScheduleViewModel {
self.diagnostics = newDiagnostics
AnalyticsManager.shared.track(.scheduleViewed(sports: Array(selectedSports).map(\.rawValue)))
AnalyticsManager.shared.track(.scheduleViewed(sports: Array(querySports).map(\.rawValue)))
logger.info("📅 Returned \(self.games.count) games")
for (sport, count) in sportCounts.sorted(by: { $0.key.rawValue < $1.key.rawValue }) {
logger.info("📅 \(sport.rawValue): \(count) games")
@@ -133,15 +163,18 @@ final class ScheduleViewModel {
#endif
} catch let cloudKitError as CloudKitError {
guard !isStaleLoad(requestID) else { return }
self.error = cloudKitError
self.errorMessage = cloudKitError.errorDescription
logger.error("📅 CloudKit error: \(cloudKitError.errorDescription ?? "unknown")")
} catch {
guard !isStaleLoad(requestID) else { return }
self.error = error
self.errorMessage = error.localizedDescription
logger.error("📅 Error loading games: \(error.localizedDescription)")
}
guard !isStaleLoad(requestID) else { return }
isLoading = false
updateFilteredGames()
}
@@ -165,15 +198,17 @@ final class ScheduleViewModel {
func resetFilters() {
selectedSports = Set(Sport.supported)
searchText = ""
startDate = Date()
endDate = Calendar.current.date(byAdding: .day, value: 14, to: Date()) ?? Date()
let defaults = Self.defaultDateRange()
startDate = defaults.start
endDate = defaults.end
loadTask?.cancel()
loadTask = Task { await loadGames() }
}
func updateDateRange(start: Date, end: Date) {
startDate = start
endDate = end
let normalized = Self.normalizedDateRange(start: start, end: end)
startDate = normalized.start
endDate = normalized.end
loadTask?.cancel()
loadTask = Task { await loadGames() }
}
@@ -212,6 +247,16 @@ final class ScheduleViewModel {
return lhs.game.dateTime < rhs.game.dateTime
}) }
}
private func beginLoadRequest() -> UUID {
let requestID = UUID()
latestLoadRequestID = requestID
return requestID
}
private func isStaleLoad(_ requestID: UUID) -> Bool {
Task.isCancelled || latestLoadRequestID != requestID
}
}
// MARK: - Diagnostics Model