iOS: device sync (Swift port of the Android engine) behind the sync_service channel

PROPFIND walk / diff / GET / PUT mirror with conflicts, per-account state,
BGTaskScheduler background runs, and conflict notifications with Keep local /
Use server. Reviewed by Codex against the Kotlin original; its data-loss
findings are fixed and each has a regression test (fake-server end-to-end
tests, 68 Swift tests total): truncated/unusable PROPFIND answers are never a
manifest, unreadable state aborts the run, server paths can't leave the mirror,
case/Unicode collisions are skipped, atomic downloads, a locally edited file
deleted on the server is kept, '/' scope matching, serialised removal,
tracked/cancellable background runs, per-account conflicts, and sign-out vs
background-off credentials (cancel gains a 'forget' flag).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Ayushya Amitabh
2026-10-06 21:01:08 -04:00
co-authored by Claude Sonnet 5.5
parent 1634d34341
commit 8805bafb97
14 changed files with 2834 additions and 10 deletions
+14
View File
@@ -9,11 +9,25 @@ import UserNotifications
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
TransferManager.shared.reconnect()
// BGTask handlers have to be registered before launch finishes.
SyncCoordinator.shared.registerBackgroundTasks()
SyncCoordinator.shared.registerNotificationCategories()
let launched = super.application(application, didFinishLaunchingWithOptions: launchOptions)
UNUserNotificationCenter.current().delegate = self
SyncCoordinator.shared.scheduleBackgroundWork()
return launched
}
/// Taps on a sync-conflict notification's "Keep local" / "Use server".
override func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
if SyncCoordinator.shared.handleNotificationResponse(response, completion: completionHandler) { return }
super.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
}
/// A transfer summary ("Downloaded 1 file") usually finishes while the app
/// is open; without this iOS delivers it silently to Notification Center
/// instead of showing the banner.