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:
Trey t
2026-01-13 18:27:56 -06:00
parent dc278085de
commit f180e5bfed
10 changed files with 2080 additions and 161 deletions

View File

@@ -418,6 +418,27 @@ actor CloudKitService {
}
}
// MARK: - Sport Sync
/// Fetch sports for sync operations
/// - Parameter lastSync: If nil, fetches all sports. If provided, fetches only sports modified since that date.
func fetchSportsForSync(since lastSync: Date?) async throws -> [CanonicalSport] {
let predicate: NSPredicate
if let lastSync = lastSync {
predicate = NSPredicate(format: "modificationDate >= %@", lastSync as NSDate)
} else {
predicate = NSPredicate(value: true)
}
let query = CKQuery(recordType: CKRecordType.sport, predicate: predicate)
let (results, _) = try await publicDatabase.records(matching: query)
return results.compactMap { result -> CanonicalSport? in
guard case .success(let record) = result.1 else { return nil }
return CKSport(record: record).toCanonical()
}
}
// MARK: - Sync Status
func checkAccountStatus() async -> CKAccountStatus {