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:
co-authored by
Claude Sonnet 5.5
parent
1634d34341
commit
8805bafb97
@@ -105,12 +105,17 @@ class SyncService {
|
||||
)
|
||||
: _SyncConfig.fromPrefs(prefs, store.accountPrefKey, account.id);
|
||||
final password = await store.readPassword(account.id);
|
||||
if (password == null ||
|
||||
config.paths.isEmpty ||
|
||||
config.intervalMinutes == null) {
|
||||
if (password == null || config.paths.isEmpty) {
|
||||
await cancelAccount(account.id);
|
||||
continue;
|
||||
}
|
||||
if (config.intervalMinutes == null) {
|
||||
// Background sync is off, but this account still has paths to sync
|
||||
// by hand: keep its credentials (native uses them for "Sync now"
|
||||
// and conflict notification actions).
|
||||
await cancelAccount(account.id, forget: false);
|
||||
continue;
|
||||
}
|
||||
await invokeIfAvailable(_channel, 'reschedule', {
|
||||
'accountId': account.id,
|
||||
'serverUrl': account.serverUrl,
|
||||
@@ -132,8 +137,17 @@ class SyncService {
|
||||
|
||||
/// Stops [accountId]'s periodic job (the account was removed, or has
|
||||
/// nothing left to sync).
|
||||
static Future<void> cancelAccount(String accountId) async {
|
||||
await invokeIfAvailable(_channel, 'cancel', {'accountId': accountId});
|
||||
///
|
||||
/// [forget] (the default) is for an account that was signed out or removed:
|
||||
/// native also drops its stored credentials. Pass false when only the
|
||||
/// background job is being turned off - the account's paths are still
|
||||
/// synced by hand ("Sync now"), and a conflict notification's actions
|
||||
/// still need its credentials.
|
||||
static Future<void> cancelAccount(String accountId, {bool forget = true}) async {
|
||||
await invokeIfAvailable(_channel, 'cancel', {
|
||||
'accountId': accountId,
|
||||
'forget': forget,
|
||||
});
|
||||
}
|
||||
|
||||
/// Runs a one-off sync pass immediately, independent of the periodic
|
||||
|
||||
Reference in New Issue
Block a user