16-task TDD implementation plan for: - CloudKit delta sync using modificationDate - Remove 90-day game browsing limit - Rename fetch* to filter* for clarity - Add allGames/allRichGames methods Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
681 lines
18 KiB
Markdown
681 lines
18 KiB
Markdown
# Delta Sync Implementation Plan
|
|
|
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
|
|
**Goal:** Remove arbitrary date restrictions from game browsing and implement proper delta sync using CloudKit modificationDate.
|
|
|
|
**Architecture:** Update CloudKitService to query by `modificationDate` instead of game `dateTime`. First sync fetches all records, subsequent syncs fetch only modified records. Rename DataProvider methods to clarify local vs network semantics.
|
|
|
|
**Tech Stack:** Swift, SwiftData, CloudKit, XCTest
|
|
|
|
---
|
|
|
|
## Task 1: Update CloudKitService.fetchStadiumsForSync
|
|
|
|
**Files:**
|
|
- Modify: `SportsTime/Core/Services/CloudKitService.swift:217-231`
|
|
|
|
**Step 1: Update method signature and implementation**
|
|
|
|
Change from:
|
|
```swift
|
|
func fetchStadiumsForSync() async throws -> [SyncStadium]
|
|
```
|
|
|
|
To:
|
|
```swift
|
|
/// 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)
|
|
|
|
return results.compactMap { result -> SyncStadium? in
|
|
guard case .success(let record) = result.1 else { return nil }
|
|
let ckStadium = CKStadium(record: record)
|
|
guard let stadium = ckStadium.stadium,
|
|
let canonicalId = ckStadium.canonicalId
|
|
else { return nil }
|
|
return SyncStadium(stadium: stadium, canonicalId: canonicalId)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add SportsTime/Core/Services/CloudKitService.swift
|
|
git commit -m "feat(sync): add lastSync parameter to fetchStadiumsForSync"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 2: Update CloudKitService.fetchTeamsForSync
|
|
|
|
**Files:**
|
|
- Modify: `SportsTime/Core/Services/CloudKitService.swift:233-249`
|
|
|
|
**Step 1: Update method signature and implementation**
|
|
|
|
Change from per-sport to all teams with delta sync:
|
|
```swift
|
|
/// 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)
|
|
|
|
return results.compactMap { result -> SyncTeam? in
|
|
guard case .success(let record) = result.1 else { return nil }
|
|
let ckTeam = CKTeam(record: record)
|
|
guard let team = ckTeam.team,
|
|
let canonicalId = ckTeam.canonicalId,
|
|
let stadiumCanonicalId = ckTeam.stadiumCanonicalId
|
|
else { return nil }
|
|
return SyncTeam(team: team, canonicalId: canonicalId, stadiumCanonicalId: stadiumCanonicalId)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add SportsTime/Core/Services/CloudKitService.swift
|
|
git commit -m "feat(sync): change fetchTeamsForSync to delta sync (all teams, not per-sport)"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 3: Update CloudKitService.fetchGamesForSync
|
|
|
|
**Files:**
|
|
- Modify: `SportsTime/Core/Services/CloudKitService.swift:251-301`
|
|
|
|
**Step 1: Update method signature and implementation**
|
|
|
|
Change from date range to delta sync:
|
|
```swift
|
|
/// 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)
|
|
|
|
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 }
|
|
}
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add SportsTime/Core/Services/CloudKitService.swift
|
|
git commit -m "feat(sync): change fetchGamesForSync to delta sync by modificationDate"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 4: Update CanonicalSyncService.syncStadiums
|
|
|
|
**Files:**
|
|
- Modify: `SportsTime/Core/Services/CanonicalSyncService.swift:208-236`
|
|
|
|
**Step 1: Pass lastSync to CloudKit fetch**
|
|
|
|
Change line 214 from:
|
|
```swift
|
|
let syncStadiums = try await cloudKitService.fetchStadiumsForSync()
|
|
```
|
|
|
|
To:
|
|
```swift
|
|
let syncStadiums = try await cloudKitService.fetchStadiumsForSync(since: lastSync)
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add SportsTime/Core/Services/CanonicalSyncService.swift
|
|
git commit -m "feat(sync): pass lastSync to stadium sync for delta updates"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 5: Update CanonicalSyncService.syncTeams
|
|
|
|
**Files:**
|
|
- Modify: `SportsTime/Core/Services/CanonicalSyncService.swift:238-271`
|
|
|
|
**Step 1: Simplify to single CloudKit call**
|
|
|
|
Replace the for-loop that calls per-sport:
|
|
```swift
|
|
@MainActor
|
|
private func syncTeams(
|
|
context: ModelContext,
|
|
since lastSync: Date?
|
|
) async throws -> (updated: Int, skippedIncompatible: Int, skippedOlder: Int) {
|
|
// Single call for all teams (no per-sport loop)
|
|
let allSyncTeams = try await cloudKitService.fetchTeamsForSync(since: lastSync)
|
|
|
|
var updated = 0
|
|
var skippedIncompatible = 0
|
|
var skippedOlder = 0
|
|
|
|
for syncTeam in allSyncTeams {
|
|
let result = try mergeTeam(
|
|
syncTeam.team,
|
|
canonicalId: syncTeam.canonicalId,
|
|
stadiumCanonicalId: syncTeam.stadiumCanonicalId,
|
|
context: context
|
|
)
|
|
|
|
switch result {
|
|
case .applied: updated += 1
|
|
case .skippedIncompatible: skippedIncompatible += 1
|
|
case .skippedOlder: skippedOlder += 1
|
|
}
|
|
}
|
|
|
|
return (updated, skippedIncompatible, skippedOlder)
|
|
}
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add SportsTime/Core/Services/CanonicalSyncService.swift
|
|
git commit -m "feat(sync): simplify team sync to single CloudKit call with delta"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 6: Update CanonicalSyncService.syncGames
|
|
|
|
**Files:**
|
|
- Modify: `SportsTime/Core/Services/CanonicalSyncService.swift:273-311`
|
|
|
|
**Step 1: Remove date range, use delta sync**
|
|
|
|
Replace lines 278-286:
|
|
```swift
|
|
@MainActor
|
|
private func syncGames(
|
|
context: ModelContext,
|
|
since lastSync: Date?
|
|
) async throws -> (updated: Int, skippedIncompatible: Int, skippedOlder: Int) {
|
|
// Delta sync: nil = all games, Date = only modified since
|
|
let syncGames = try await cloudKitService.fetchGamesForSync(since: lastSync)
|
|
|
|
var updated = 0
|
|
var skippedIncompatible = 0
|
|
var skippedOlder = 0
|
|
|
|
for syncGame in syncGames {
|
|
let result = try mergeGame(
|
|
syncGame.game,
|
|
canonicalId: syncGame.canonicalId,
|
|
homeTeamCanonicalId: syncGame.homeTeamCanonicalId,
|
|
awayTeamCanonicalId: syncGame.awayTeamCanonicalId,
|
|
stadiumCanonicalId: syncGame.stadiumCanonicalId,
|
|
context: context
|
|
)
|
|
|
|
switch result {
|
|
case .applied: updated += 1
|
|
case .skippedIncompatible: skippedIncompatible += 1
|
|
case .skippedOlder: skippedOlder += 1
|
|
}
|
|
}
|
|
|
|
return (updated, skippedIncompatible, skippedOlder)
|
|
}
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add SportsTime/Core/Services/CanonicalSyncService.swift
|
|
git commit -m "feat(sync): remove date range from game sync, use modificationDate delta"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 7: Rename DataProvider.fetchGames to filterGames
|
|
|
|
**Files:**
|
|
- Modify: `SportsTime/Core/Services/DataProvider.swift:121-144`
|
|
|
|
**Step 1: Rename method**
|
|
|
|
Change:
|
|
```swift
|
|
/// Fetch games from SwiftData within date range
|
|
func fetchGames(sports: Set<Sport>, startDate: Date, endDate: Date) async throws -> [Game]
|
|
```
|
|
|
|
To:
|
|
```swift
|
|
/// Filter games from SwiftData within date range
|
|
func filterGames(sports: Set<Sport>, startDate: Date, endDate: Date) async throws -> [Game]
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add SportsTime/Core/Services/DataProvider.swift
|
|
git commit -m "refactor: rename fetchGames to filterGames (clarify local query)"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 8: Rename DataProvider.fetchRichGames to filterRichGames
|
|
|
|
**Files:**
|
|
- Modify: `SportsTime/Core/Services/DataProvider.swift:163-175`
|
|
|
|
**Step 1: Rename method and update internal call**
|
|
|
|
Change:
|
|
```swift
|
|
/// Fetch games with full team and stadium data
|
|
func fetchRichGames(sports: Set<Sport>, startDate: Date, endDate: Date) async throws -> [RichGame] {
|
|
let games = try await fetchGames(sports: sports, startDate: startDate, endDate: endDate)
|
|
```
|
|
|
|
To:
|
|
```swift
|
|
/// Filter games with full team and stadium data within date range
|
|
func filterRichGames(sports: Set<Sport>, startDate: Date, endDate: Date) async throws -> [RichGame] {
|
|
let games = try await filterGames(sports: sports, startDate: startDate, endDate: endDate)
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add SportsTime/Core/Services/DataProvider.swift
|
|
git commit -m "refactor: rename fetchRichGames to filterRichGames"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 9: Add DataProvider.allGames method
|
|
|
|
**Files:**
|
|
- Modify: `SportsTime/Core/Services/DataProvider.swift` (add after filterGames, around line 145)
|
|
|
|
**Step 1: Add new method**
|
|
|
|
```swift
|
|
/// Get all games for specified sports (no date filtering)
|
|
func allGames(for sports: Set<Sport>) async throws -> [Game] {
|
|
guard let context = modelContext else {
|
|
throw DataProviderError.contextNotConfigured
|
|
}
|
|
|
|
let sportStrings = sports.map { $0.rawValue }
|
|
|
|
let descriptor = FetchDescriptor<CanonicalGame>(
|
|
predicate: #Predicate<CanonicalGame> { game in
|
|
game.deprecatedAt == nil
|
|
},
|
|
sortBy: [SortDescriptor(\.dateTime)]
|
|
)
|
|
|
|
let canonicalGames = try context.fetch(descriptor)
|
|
|
|
return canonicalGames.compactMap { canonical -> Game? in
|
|
guard sportStrings.contains(canonical.sport) else { return nil }
|
|
return canonical.toDomain()
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add SportsTime/Core/Services/DataProvider.swift
|
|
git commit -m "feat: add allGames method for unfiltered game access"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 10: Add DataProvider.allRichGames method
|
|
|
|
**Files:**
|
|
- Modify: `SportsTime/Core/Services/DataProvider.swift` (add after allGames)
|
|
|
|
**Step 1: Add new method**
|
|
|
|
```swift
|
|
/// Get all games with full team and stadium data (no date filtering)
|
|
func allRichGames(for sports: Set<Sport>) async throws -> [RichGame] {
|
|
let games = try await allGames(for: sports)
|
|
|
|
return games.compactMap { game in
|
|
guard let homeTeam = teamsById[game.homeTeamId],
|
|
let awayTeam = teamsById[game.awayTeamId],
|
|
let stadium = stadiumsById[game.stadiumId] else {
|
|
return nil
|
|
}
|
|
return RichGame(game: game, homeTeam: homeTeam, awayTeam: awayTeam, stadium: stadium)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add SportsTime/Core/Services/DataProvider.swift
|
|
git commit -m "feat: add allRichGames method for unfiltered rich game access"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 11: Update TripCreationViewModel.loadGamesForBrowsing
|
|
|
|
**Files:**
|
|
- Modify: `SportsTime/Features/Trip/ViewModels/TripCreationViewModel.swift:491-497`
|
|
|
|
**Step 1: Remove 90-day limit, use allGames**
|
|
|
|
Change from:
|
|
```swift
|
|
// Fetch games for next 90 days for browsing
|
|
let browseEndDate = Calendar.current.date(byAdding: .day, value: 90, to: Date()) ?? endDate
|
|
games = try await dataProvider.fetchGames(
|
|
sports: selectedSports,
|
|
startDate: Date(),
|
|
endDate: browseEndDate
|
|
)
|
|
```
|
|
|
|
To:
|
|
```swift
|
|
// Fetch ALL available games for browsing (no date restrictions)
|
|
games = try await dataProvider.allGames(for: selectedSports)
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add SportsTime/Features/Trip/ViewModels/TripCreationViewModel.swift
|
|
git commit -m "feat: remove 90-day limit from game browsing, show all games"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 12: Update MockCloudKitService sync methods
|
|
|
|
**Files:**
|
|
- Modify: `SportsTimeTests/Mocks/MockCloudKitService.swift:165-206`
|
|
|
|
**Step 1: Update fetchStadiumsForSync**
|
|
|
|
```swift
|
|
func fetchStadiumsForSync(since lastSync: Date?) async throws -> [CloudKitService.SyncStadium] {
|
|
try await simulateNetwork()
|
|
let filtered = lastSync == nil ? stadiums : stadiums // Mock doesn't track modificationDate
|
|
return filtered.map { stadium in
|
|
CloudKitService.SyncStadium(
|
|
stadium: stadium,
|
|
canonicalId: stadium.id
|
|
)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Update fetchTeamsForSync**
|
|
|
|
```swift
|
|
func fetchTeamsForSync(since lastSync: Date?) async throws -> [CloudKitService.SyncTeam] {
|
|
try await simulateNetwork()
|
|
return teams.map { team in
|
|
CloudKitService.SyncTeam(
|
|
team: team,
|
|
canonicalId: team.id,
|
|
stadiumCanonicalId: team.stadiumId
|
|
)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 3: Update fetchGamesForSync**
|
|
|
|
```swift
|
|
func fetchGamesForSync(since lastSync: Date?) async throws -> [CloudKitService.SyncGame] {
|
|
try await simulateNetwork()
|
|
|
|
return games.map { game in
|
|
CloudKitService.SyncGame(
|
|
game: game,
|
|
canonicalId: game.id,
|
|
homeTeamCanonicalId: game.homeTeamId,
|
|
awayTeamCanonicalId: game.awayTeamId,
|
|
stadiumCanonicalId: game.stadiumId
|
|
)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 4: Commit**
|
|
|
|
```bash
|
|
git add SportsTimeTests/Mocks/MockCloudKitService.swift
|
|
git commit -m "test: update MockCloudKitService signatures for delta sync"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 13: Update MockAppDataProvider methods
|
|
|
|
**Files:**
|
|
- Modify: `SportsTimeTests/Mocks/MockAppDataProvider.swift:157-189`
|
|
|
|
**Step 1: Rename fetchGames to filterGames**
|
|
|
|
```swift
|
|
func filterGames(sports: Set<Sport>, startDate: Date, endDate: Date) async throws -> [Game] {
|
|
fetchGamesCallCount += 1
|
|
await simulateLatency()
|
|
|
|
if config.shouldFailOnFetch {
|
|
throw DataProviderError.contextNotConfigured
|
|
}
|
|
|
|
return games.filter { game in
|
|
sports.contains(game.sport) &&
|
|
game.dateTime >= startDate &&
|
|
game.dateTime <= endDate
|
|
}.sorted { $0.dateTime < $1.dateTime }
|
|
}
|
|
```
|
|
|
|
**Step 2: Rename fetchRichGames to filterRichGames**
|
|
|
|
```swift
|
|
func filterRichGames(sports: Set<Sport>, startDate: Date, endDate: Date) async throws -> [RichGame] {
|
|
fetchRichGamesCallCount += 1
|
|
let filteredGames = try await filterGames(sports: sports, startDate: startDate, endDate: endDate)
|
|
|
|
return filteredGames.compactMap { game in
|
|
richGame(from: game)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 3: Add allGames method**
|
|
|
|
```swift
|
|
func allGames(for sports: Set<Sport>) async throws -> [Game] {
|
|
fetchGamesCallCount += 1
|
|
await simulateLatency()
|
|
|
|
if config.shouldFailOnFetch {
|
|
throw DataProviderError.contextNotConfigured
|
|
}
|
|
|
|
return games.filter { game in
|
|
sports.contains(game.sport)
|
|
}.sorted { $0.dateTime < $1.dateTime }
|
|
}
|
|
```
|
|
|
|
**Step 4: Add allRichGames method**
|
|
|
|
```swift
|
|
func allRichGames(for sports: Set<Sport>) async throws -> [RichGame] {
|
|
fetchRichGamesCallCount += 1
|
|
let allGames = try await allGames(for: sports)
|
|
|
|
return allGames.compactMap { game in
|
|
richGame(from: game)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add SportsTimeTests/Mocks/MockAppDataProvider.swift
|
|
git commit -m "test: update MockAppDataProvider with renamed and new methods"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 14: Fix all callers of renamed methods
|
|
|
|
**Files:**
|
|
- Search and update all files calling the old method names
|
|
|
|
**Step 1: Find all usages**
|
|
|
|
Run:
|
|
```bash
|
|
grep -r "fetchGames\|fetchRichGames" --include="*.swift" SportsTime/ SportsTimeTests/ | grep -v "Mock"
|
|
```
|
|
|
|
**Step 2: Update each caller**
|
|
|
|
For each file found:
|
|
- `fetchGames(` → `filterGames(` (when using date parameters)
|
|
- `fetchRichGames(` → `filterRichGames(` (when using date parameters)
|
|
|
|
Common files likely to need updates:
|
|
- `GameMatcher.swift`
|
|
- `ScheduleMatcher.swift`
|
|
- `TripPlanningEngine.swift`
|
|
- Various test files
|
|
|
|
**Step 3: Commit**
|
|
|
|
```bash
|
|
git add -A
|
|
git commit -m "refactor: update all callers to use renamed filter methods"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 15: Run tests and fix any failures
|
|
|
|
**Step 1: Run full test suite**
|
|
|
|
```bash
|
|
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' test
|
|
```
|
|
|
|
**Step 2: Fix any compilation errors or test failures**
|
|
|
|
Most likely issues:
|
|
- Missing method implementations
|
|
- Signature mismatches between mock and real implementations
|
|
- Tests expecting old method names
|
|
|
|
**Step 3: Commit fixes**
|
|
|
|
```bash
|
|
git add -A
|
|
git commit -m "fix: resolve test failures from delta sync refactor"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 16: Final verification
|
|
|
|
**Step 1: Verify CloudKit sync logic**
|
|
|
|
Review the sync flow:
|
|
1. First launch: `lastSync == nil` → fetches ALL records
|
|
2. Subsequent syncs: `lastSync == Date` → fetches only modified records
|
|
|
|
**Step 2: Verify game browsing**
|
|
|
|
1. Build and run app
|
|
2. Go to "By Games" mode
|
|
3. Select MLB
|
|
4. Verify Houston Astros shows full season (160+ games)
|
|
|
|
**Step 3: Final commit**
|
|
|
|
```bash
|
|
git add -A
|
|
git commit -m "feat: complete delta sync and unlimited game browsing implementation"
|
|
```
|
|
|
|
---
|
|
|
|
## Summary of Changes
|
|
|
|
| File | Changes |
|
|
|------|---------|
|
|
| `CloudKitService.swift` | 3 methods updated to use `since: Date?` parameter |
|
|
| `CanonicalSyncService.swift` | 3 methods updated to pass `lastSync` |
|
|
| `DataProvider.swift` | 2 methods renamed, 2 methods added |
|
|
| `TripCreationViewModel.swift` | 1 method updated to use `allGames` |
|
|
| `MockCloudKitService.swift` | 3 methods updated to match new signatures |
|
|
| `MockAppDataProvider.swift` | 2 methods renamed, 2 methods added |
|
|
| Various callers | Updated to use new method names |
|