- Three-scenario planning engine (A: date range, B: selected games, C: directional routes) - GeographicRouteExplorer with anchor game support for route exploration - Shared ItineraryBuilder for travel segment calculation - TravelEstimator for driving time/distance estimation - SwiftUI views for trip creation and detail display - CloudKit integration for schedule data - Python scraping scripts for sports schedules 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
109 lines
3.4 KiB
Swift
109 lines
3.4 KiB
Swift
//
|
|
// CloudKitDataProvider.swift
|
|
// SportsTime
|
|
//
|
|
// Wraps CloudKitService to conform to DataProvider protocol
|
|
//
|
|
|
|
import Foundation
|
|
|
|
actor CloudKitDataProvider: DataProvider {
|
|
|
|
private let cloudKit = CloudKitService.shared
|
|
|
|
// MARK: - Availability
|
|
|
|
func checkAvailability() async throws {
|
|
try await cloudKit.checkAvailabilityWithError()
|
|
}
|
|
|
|
// MARK: - DataProvider Protocol
|
|
|
|
func fetchTeams(for sport: Sport) async throws -> [Team] {
|
|
do {
|
|
try await checkAvailability()
|
|
return try await cloudKit.fetchTeams(for: sport)
|
|
} catch {
|
|
throw CloudKitError.from(error)
|
|
}
|
|
}
|
|
|
|
func fetchAllTeams() async throws -> [Team] {
|
|
do {
|
|
try await checkAvailability()
|
|
var allTeams: [Team] = []
|
|
for sport in Sport.supported {
|
|
let teams = try await cloudKit.fetchTeams(for: sport)
|
|
allTeams.append(contentsOf: teams)
|
|
}
|
|
return allTeams
|
|
} catch {
|
|
throw CloudKitError.from(error)
|
|
}
|
|
}
|
|
|
|
func fetchStadiums() async throws -> [Stadium] {
|
|
do {
|
|
try await checkAvailability()
|
|
return try await cloudKit.fetchStadiums()
|
|
} catch {
|
|
throw CloudKitError.from(error)
|
|
}
|
|
}
|
|
|
|
func fetchGames(sports: Set<Sport>, startDate: Date, endDate: Date) async throws -> [Game] {
|
|
do {
|
|
try await checkAvailability()
|
|
return try await cloudKit.fetchGames(sports: sports, startDate: startDate, endDate: endDate)
|
|
} catch {
|
|
throw CloudKitError.from(error)
|
|
}
|
|
}
|
|
|
|
func fetchGame(by id: UUID) async throws -> Game? {
|
|
do {
|
|
try await checkAvailability()
|
|
return try await cloudKit.fetchGame(by: id)
|
|
} catch {
|
|
throw CloudKitError.from(error)
|
|
}
|
|
}
|
|
|
|
func fetchRichGames(sports: Set<Sport>, startDate: Date, endDate: Date) async throws -> [RichGame] {
|
|
do {
|
|
try await checkAvailability()
|
|
|
|
// Fetch all required data
|
|
async let gamesTask = cloudKit.fetchGames(sports: sports, startDate: startDate, endDate: endDate)
|
|
async let teamsTask = fetchAllTeamsInternal()
|
|
async let stadiumsTask = cloudKit.fetchStadiums()
|
|
|
|
let (games, teams, stadiums) = try await (gamesTask, teamsTask, stadiumsTask)
|
|
|
|
let teamsById = Dictionary(uniqueKeysWithValues: teams.map { ($0.id, $0) })
|
|
let stadiumsById = Dictionary(uniqueKeysWithValues: stadiums.map { ($0.id, $0) })
|
|
|
|
return games.compactMap { game in
|
|
guard let homeTeam = teamsById[game.homeTeamId],
|
|
let awayTeam = teamsById[game.awayTeamId],
|
|
let stadium = stadiumsById[game.stadiumId] else {
|
|
return nil
|
|
}
|
|
return RichGame(game: game, homeTeam: homeTeam, awayTeam: awayTeam, stadium: stadium)
|
|
}
|
|
} catch {
|
|
throw CloudKitError.from(error)
|
|
}
|
|
}
|
|
|
|
// Internal helper to avoid duplicate availability checks
|
|
private func fetchAllTeamsInternal() async throws -> [Team] {
|
|
var allTeams: [Team] = []
|
|
for sport in Sport.supported {
|
|
let teams = try await cloudKit.fetchTeams(for: sport)
|
|
allTeams.append(contentsOf: teams)
|
|
}
|
|
return allTeams
|
|
}
|
|
}
|