feat(sync): update CloudKit sync methods to use modificationDate delta

- fetchStadiumsForSync now accepts since: Date? parameter
- fetchTeamsForSync changed from per-sport to all teams with delta sync
- fetchGamesForSync uses modificationDate instead of game dateTime

When lastSync is nil, fetches all records (first sync).
When lastSync has value, fetches only modified records (delta sync).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-12 10:48:34 -06:00
parent b514d2119c
commit 3bb903ab09

View File

@@ -213,9 +213,15 @@ actor CloudKitService {
// MARK: - Sync Fetch Methods (return canonical IDs directly from CloudKit)
/// Fetch stadiums with canonical IDs for sync operations
func fetchStadiumsForSync() async throws -> [SyncStadium] {
let predicate = NSPredicate(value: true)
/// Fetch stadiums for sync operations
/// - Parameter lastSync: If nil, fetches all stadiums. If provided, fetches only stadiums modified since that date.
func fetchStadiumsForSync(since lastSync: Date?) async throws -> [SyncStadium] {
let predicate: NSPredicate
if let lastSync = lastSync {
predicate = NSPredicate(format: "modificationDate >= %@", lastSync as NSDate)
} else {
predicate = NSPredicate(value: true)
}
let query = CKQuery(recordType: CKRecordType.stadium, predicate: predicate)
let (results, _) = try await publicDatabase.records(matching: query)
@@ -230,9 +236,15 @@ actor CloudKitService {
}
}
/// Fetch teams with canonical IDs for sync operations
func fetchTeamsForSync(for sport: Sport) async throws -> [SyncTeam] {
let predicate = NSPredicate(format: "sport == %@", sport.rawValue)
/// Fetch teams for sync operations
/// - Parameter lastSync: If nil, fetches all teams. If provided, fetches only teams modified since that date.
func fetchTeamsForSync(since lastSync: Date?) async throws -> [SyncTeam] {
let predicate: NSPredicate
if let lastSync = lastSync {
predicate = NSPredicate(format: "modificationDate >= %@", lastSync as NSDate)
} else {
predicate = NSPredicate(value: true)
}
let query = CKQuery(recordType: CKRecordType.team, predicate: predicate)
let (results, _) = try await publicDatabase.records(matching: query)
@@ -248,56 +260,43 @@ actor CloudKitService {
}
}
/// Fetch games with canonical IDs for sync operations
func fetchGamesForSync(
sports: Set<Sport>,
startDate: Date,
endDate: Date
) async throws -> [SyncGame] {
var allGames: [SyncGame] = []
for sport in sports {
let predicate = NSPredicate(
format: "sport == %@ AND dateTime >= %@ AND dateTime <= %@",
sport.rawValue,
startDate as NSDate,
endDate as NSDate
)
let query = CKQuery(recordType: CKRecordType.game, predicate: predicate)
let (results, _) = try await publicDatabase.records(matching: query)
let games = results.compactMap { result -> SyncGame? in
guard case .success(let record) = result.1 else { return nil }
let ckGame = CKGame(record: record)
// Extract canonical IDs directly from CloudKit
guard let canonicalId = ckGame.canonicalId,
let homeTeamCanonicalId = ckGame.homeTeamCanonicalId,
let awayTeamCanonicalId = ckGame.awayTeamCanonicalId,
let stadiumCanonicalId = ckGame.stadiumCanonicalId
else { return nil }
// For the Game domain object, use canonical IDs directly
guard let game = ckGame.game(
homeTeamId: homeTeamCanonicalId,
awayTeamId: awayTeamCanonicalId,
stadiumId: stadiumCanonicalId
) else { return nil }
return SyncGame(
game: game,
canonicalId: canonicalId,
homeTeamCanonicalId: homeTeamCanonicalId,
awayTeamCanonicalId: awayTeamCanonicalId,
stadiumCanonicalId: stadiumCanonicalId
)
}
allGames.append(contentsOf: games)
/// Fetch games for sync operations
/// - Parameter lastSync: If nil, fetches all games. If provided, fetches only games modified since that date.
func fetchGamesForSync(since lastSync: Date?) async throws -> [SyncGame] {
let predicate: NSPredicate
if let lastSync = lastSync {
predicate = NSPredicate(format: "modificationDate >= %@", lastSync as NSDate)
} else {
predicate = NSPredicate(value: true)
}
let query = CKQuery(recordType: CKRecordType.game, predicate: predicate)
return allGames.sorted { $0.game.dateTime < $1.game.dateTime }
let (results, _) = try await publicDatabase.records(matching: query)
return results.compactMap { result -> SyncGame? in
guard case .success(let record) = result.1 else { return nil }
let ckGame = CKGame(record: record)
guard let canonicalId = ckGame.canonicalId,
let homeTeamCanonicalId = ckGame.homeTeamCanonicalId,
let awayTeamCanonicalId = ckGame.awayTeamCanonicalId,
let stadiumCanonicalId = ckGame.stadiumCanonicalId
else { return nil }
guard let game = ckGame.game(
homeTeamId: homeTeamCanonicalId,
awayTeamId: awayTeamCanonicalId,
stadiumId: stadiumCanonicalId
) else { return nil }
return SyncGame(
game: game,
canonicalId: canonicalId,
homeTeamCanonicalId: homeTeamCanonicalId,
awayTeamCanonicalId: awayTeamCanonicalId,
stadiumCanonicalId: stadiumCanonicalId
)
}.sorted { $0.game.dateTime < $1.game.dateTime }
}
// MARK: - League Structure & Team Aliases