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

@@ -481,6 +481,66 @@ final class CanonicalGame {
}
}
// MARK: - Canonical Sport
@Model
final class CanonicalSport {
@Attribute(.unique) var id: String
var abbreviation: String
var displayName: String
var iconName: String
var colorHex: String
var seasonStartMonth: Int
var seasonEndMonth: Int
var isActive: Bool
var lastModified: Date
var schemaVersion: Int
var sourceRaw: String
init(
id: String,
abbreviation: String,
displayName: String,
iconName: String,
colorHex: String,
seasonStartMonth: Int,
seasonEndMonth: Int,
isActive: Bool = true,
lastModified: Date = Date(),
schemaVersion: Int = SchemaVersion.current,
source: DataSource = .cloudKit
) {
self.id = id
self.abbreviation = abbreviation
self.displayName = displayName
self.iconName = iconName
self.colorHex = colorHex
self.seasonStartMonth = seasonStartMonth
self.seasonEndMonth = seasonEndMonth
self.isActive = isActive
self.lastModified = lastModified
self.schemaVersion = schemaVersion
self.sourceRaw = source.rawValue
}
var source: DataSource {
get { DataSource(rawValue: sourceRaw) ?? .cloudKit }
set { sourceRaw = newValue.rawValue }
}
func toDomain() -> DynamicSport {
DynamicSport(
id: id,
abbreviation: abbreviation,
displayName: displayName,
iconName: iconName,
colorHex: colorHex,
seasonStartMonth: seasonStartMonth,
seasonEndMonth: seasonEndMonth
)
}
}
// MARK: - Bundled Data Timestamps
/// Timestamps for bundled data files.