feat(sync): add CloudKit sync for dynamic sports
- Add CKSport model to parse CloudKit Sport records - Add fetchSportsForSync() to CloudKitService for delta fetching - Add syncSports() and mergeSport() to CanonicalSyncService - Update DataProvider with dynamicSports support and allSports computed property - Update MockAppDataProvider with matching dynamic sports support - Add comprehensive documentation for adding new sports The app can now sync sport definitions from CloudKit, enabling new sports to be added without app updates. Sports are fetched, merged into SwiftData, and exposed via AppDataProvider.allSports alongside built-in Sport enum cases. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ final class MockAppDataProvider: ObservableObject {
|
||||
|
||||
@Published private(set) var teams: [Team] = []
|
||||
@Published private(set) var stadiums: [Stadium] = []
|
||||
@Published private(set) var dynamicSports: [DynamicSport] = []
|
||||
@Published private(set) var isLoading = false
|
||||
@Published private(set) var error: Error?
|
||||
@Published private(set) var errorMessage: String?
|
||||
@@ -26,6 +27,7 @@ final class MockAppDataProvider: ObservableObject {
|
||||
|
||||
private var teamsById: [String: Team] = [:]
|
||||
private var stadiumsById: [String: Stadium] = [:]
|
||||
private var dynamicSportsById: [String: DynamicSport] = [:]
|
||||
private var games: [Game] = []
|
||||
private var gamesById: [String: Game] = [:]
|
||||
|
||||
@@ -80,12 +82,19 @@ final class MockAppDataProvider: ObservableObject {
|
||||
self.gamesById = Dictionary(uniqueKeysWithValues: newGames.map { ($0.id, $0) })
|
||||
}
|
||||
|
||||
func setDynamicSports(_ newSports: [DynamicSport]) {
|
||||
self.dynamicSports = newSports
|
||||
self.dynamicSportsById = Dictionary(uniqueKeysWithValues: newSports.map { ($0.id, $0) })
|
||||
}
|
||||
|
||||
func reset() {
|
||||
teams = []
|
||||
stadiums = []
|
||||
dynamicSports = []
|
||||
games = []
|
||||
teamsById = [:]
|
||||
stadiumsById = [:]
|
||||
dynamicSportsById = [:]
|
||||
gamesById = [:]
|
||||
isLoading = false
|
||||
error = nil
|
||||
@@ -156,6 +165,17 @@ final class MockAppDataProvider: ObservableObject {
|
||||
teams.filter { $0.sport == sport }
|
||||
}
|
||||
|
||||
func dynamicSport(for id: String) -> DynamicSport? {
|
||||
dynamicSportsById[id]
|
||||
}
|
||||
|
||||
/// All sports: built-in Sport enum cases + CloudKit-defined DynamicSports
|
||||
var allSports: [any AnySport] {
|
||||
let builtIn: [any AnySport] = Sport.allCases
|
||||
let dynamic: [any AnySport] = dynamicSports
|
||||
return builtIn + dynamic
|
||||
}
|
||||
|
||||
// MARK: - Game Filtering (Local Queries)
|
||||
|
||||
func filterGames(sports: Set<Sport>, startDate: Date, endDate: Date) async throws -> [Game] {
|
||||
@@ -287,4 +307,13 @@ extension MockAppDataProvider {
|
||||
|
||||
/// Get stadiums count
|
||||
var stadiumsCount: Int { stadiums.count }
|
||||
|
||||
/// Add a single dynamic sport
|
||||
func addDynamicSport(_ sport: DynamicSport) {
|
||||
dynamicSports.append(sport)
|
||||
dynamicSportsById[sport.id] = sport
|
||||
}
|
||||
|
||||
/// Get dynamic sports count
|
||||
var dynamicSportsCount: Int { dynamicSports.count }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user