Stabilize beta release with warning cleanup and edge-case fixes

This commit is contained in:
Trey t
2026-02-22 13:18:14 -06:00
parent fddea81e36
commit ec2bbb4764
55 changed files with 712 additions and 315 deletions

View File

@@ -13,7 +13,7 @@ actor ItineraryItemService {
// Debounce tracking
private var pendingUpdates: [UUID: ItineraryItem] = [:]
private var retryCount: [UUID: Int] = [:]
private var debounceTask: Task<Void, Never>?
private var flushTask: Task<Void, Never>?
private let maxRetries = 3
private let debounceInterval: Duration = .seconds(1.5)
@@ -22,8 +22,8 @@ actor ItineraryItemService {
/// Cancel any pending debounce task (for cleanup)
func cancelPendingSync() {
debounceTask?.cancel()
debounceTask = nil
flushTask?.cancel()
flushTask = nil
}
// MARK: - CRUD Operations
@@ -52,26 +52,7 @@ actor ItineraryItemService {
func updateItem(_ item: ItineraryItem) async {
pendingUpdates[item.id] = item
// Cancel existing debounce task
debounceTask?.cancel()
// Start new debounce task with proper cancellation handling
debounceTask = Task {
// Check cancellation before sleeping
guard !Task.isCancelled else { return }
do {
try await Task.sleep(for: debounceInterval)
} catch {
// Task was cancelled during sleep
return
}
// Check cancellation after sleeping (belt and suspenders)
guard !Task.isCancelled else { return }
await flushPendingUpdates()
}
scheduleFlush(after: debounceInterval)
}
/// Force immediate sync of pending updates
@@ -92,15 +73,47 @@ actor ItineraryItemService {
retryCount[item.id] = nil
}
} catch {
// Add back to pending for retry, respecting max retry limit
// Keep failed updates queued and retry with backoff; never drop user edits.
var highestRetry = 0
for item in updates.values {
let currentRetries = retryCount[item.id] ?? 0
if currentRetries < maxRetries {
pendingUpdates[item.id] = item
retryCount[item.id] = currentRetries + 1
}
// If max retries exceeded, silently drop the update to prevent infinite loop
let nextRetry = min(currentRetries + 1, maxRetries)
retryCount[item.id] = nextRetry
highestRetry = max(highestRetry, nextRetry)
pendingUpdates[item.id] = item
}
let retryDelay = retryDelay(for: highestRetry)
scheduleFlush(after: retryDelay)
}
}
// MARK: - Flush Scheduling
private func scheduleFlush(after delay: Duration) {
flushTask?.cancel()
flushTask = Task {
guard !Task.isCancelled else { return }
do {
try await Task.sleep(for: delay)
} catch {
return
}
guard !Task.isCancelled else { return }
await flushPendingUpdates()
}
}
private func retryDelay(for retryLevel: Int) -> Duration {
switch retryLevel {
case 0, 1:
return .seconds(5)
case 2:
return .seconds(15)
default:
return .seconds(30)
}
}
@@ -116,7 +129,7 @@ actor ItineraryItemService {
for item in items {
let recordId = CKRecord.ID(recordName: item.id.uuidString)
try? await database.deleteRecord(withID: recordId)
_ = try? await database.deleteRecord(withID: recordId)
}
}
}