Add StadiumAlias CloudKit sync and offline-first data architecture
- Add CKStadiumAlias model for CloudKit record mapping - Add fetchStadiumAliases/fetchStadiumAliasChanges to CloudKitService - Add syncStadiumAliases to CanonicalSyncService for delta sync - Add subscribeToStadiumAliasUpdates for push notifications - Update cloudkit_import.py with --stadium-aliases-only option Data Architecture Updates: - Remove obsolete provider files (CanonicalDataProvider, CloudKitDataProvider, StubDataProvider) - AppDataProvider now reads exclusively from SwiftData - Add background CloudKit sync on app startup (non-blocking) - Document data architecture in CLAUDE.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -228,6 +228,24 @@ actor CloudKitService {
|
||||
}
|
||||
}
|
||||
|
||||
func fetchStadiumAliases(for stadiumCanonicalId: String? = nil) async throws -> [StadiumAlias] {
|
||||
let predicate: NSPredicate
|
||||
if let stadiumId = stadiumCanonicalId {
|
||||
predicate = NSPredicate(format: "stadiumCanonicalId == %@", stadiumId)
|
||||
} else {
|
||||
predicate = NSPredicate(value: true)
|
||||
}
|
||||
|
||||
let query = CKQuery(recordType: CKRecordType.stadiumAlias, predicate: predicate)
|
||||
|
||||
let (results, _) = try await publicDatabase.records(matching: query)
|
||||
|
||||
return results.compactMap { result in
|
||||
guard case .success(let record) = result.1 else { return nil }
|
||||
return CKStadiumAlias(record: record).toModel()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Delta Sync (Date-Based for Public Database)
|
||||
|
||||
/// Fetch league structure records modified after the given date
|
||||
@@ -270,6 +288,26 @@ actor CloudKitService {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch stadium alias records modified after the given date
|
||||
func fetchStadiumAliasChanges(since lastSync: Date?) async throws -> [StadiumAlias] {
|
||||
let predicate: NSPredicate
|
||||
if let lastSync = lastSync {
|
||||
predicate = NSPredicate(format: "lastModified > %@", lastSync as NSDate)
|
||||
} else {
|
||||
predicate = NSPredicate(value: true)
|
||||
}
|
||||
|
||||
let query = CKQuery(recordType: CKRecordType.stadiumAlias, predicate: predicate)
|
||||
query.sortDescriptors = [NSSortDescriptor(key: CKStadiumAlias.lastModifiedKey, ascending: true)]
|
||||
|
||||
let (results, _) = try await publicDatabase.records(matching: query)
|
||||
|
||||
return results.compactMap { result in
|
||||
guard case .success(let record) = result.1 else { return nil }
|
||||
return CKStadiumAlias(record: record).toModel()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sync Status
|
||||
|
||||
func checkAccountStatus() async -> CKAccountStatus {
|
||||
@@ -327,10 +365,26 @@ actor CloudKitService {
|
||||
try await publicDatabase.save(subscription)
|
||||
}
|
||||
|
||||
func subscribeToStadiumAliasUpdates() async throws {
|
||||
let subscription = CKQuerySubscription(
|
||||
recordType: CKRecordType.stadiumAlias,
|
||||
predicate: NSPredicate(value: true),
|
||||
subscriptionID: "stadium-alias-updates",
|
||||
options: [.firesOnRecordCreation, .firesOnRecordUpdate]
|
||||
)
|
||||
|
||||
let notification = CKSubscription.NotificationInfo()
|
||||
notification.shouldSendContentAvailable = true
|
||||
subscription.notificationInfo = notification
|
||||
|
||||
try await publicDatabase.save(subscription)
|
||||
}
|
||||
|
||||
/// Subscribe to all canonical data updates
|
||||
func subscribeToAllUpdates() async throws {
|
||||
try await subscribeToScheduleUpdates()
|
||||
try await subscribeToLeagueStructureUpdates()
|
||||
try await subscribeToTeamAliasUpdates()
|
||||
try await subscribeToStadiumAliasUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user