Skip to content

feat(sync): inject isOnlineOverride into SyncService.isOnline()#68

Open
abhijitnairDhwani wants to merge 1 commit into
developfrom
feat/is-online-override
Open

feat(sync): inject isOnlineOverride into SyncService.isOnline()#68
abhijitnairDhwani wants to merge 1 commit into
developfrom
feat/is-online-override

Conversation

@abhijitnairDhwani

Copy link
Copy Markdown
Collaborator

What

Adds an optional Future<bool> Function()? isOnlineOverride parameter on SyncService and FrappeSDK. When set, the override replaces the connectivity_plus probe inside SyncService.isOnline(). When unset (the default), behaviour is unchanged.

Why this is needed

SyncService.isOnline() calls Connectivity().checkConnectivity() directly and only accepts mobile | wifi | ethernet:

Future<bool> isOnline() async {
  if (!offlineMode.enabled) return false;
  final connectivityResult = await Connectivity().checkConnectivity();
  return connectivityResult.contains(ConnectivityResult.mobile) ||
      connectivityResult.contains(ConnectivityResult.wifi) ||
      connectivityResult.contains(ConnectivityResult.ethernet);
}

On Android emulators with adb reverse, the platform reports ConnectivityResult.none because the tunnel isn't a recognised network type — even though HTTP traffic to the bench flows fine. pullSync / pullSyncMany / pushSync then early-exit with noConnectivity and refuse to run.

Today consumers have no app-side escape:

  • The ConnectivityWatcher isn't exported from the barrel.
  • SyncService.isOnline() bypasses the watcher anyway — it probes the plugin directly.
  • The FrappeSDK constructor takes no hook to replace the probe.

The only workaround until now has been to bypass SyncService.pullSync / pullSyncMany entirely from the consumer app — losing the SDK's delta cursors, page lookahead, and RESUME-on-crash semantics in the process. We were carrying that bypass for ~2 weeks in the Swasti V3 mobile app before this PR.

What this PR changes

  1. SyncService constructor accepts Future<bool> Function()? isOnlineOverride.
  2. SyncService.isOnline() calls the override first when set; falls back to the platform probe on throw (with a logged warning) so a buggy override can't brick sync.
  3. FrappeSDK() accepts isOnlineOverride and threads it through to SyncService.
  4. The FrappeSDK.forTesting constructor initialises the field to null in its initializer list to keep tests source-compatible.

Backward compatibility

  • Default value is null — existing FrappeSDK(baseUrl: ...) and SyncService(...) call sites behave exactly as before.
  • The existing private _isOnlineOverrideForTesting test seam in frappe_sdk.dart is unchanged.

Safety notes

  • offlineMode.enabled gate runs first; the override does not bypass an explicit offline-mode opt-out.
  • If the override throws, isOnline() logs the failure and falls through to the platform probe.

Intended use

Dev builds that talk to a local bench via adb reverse:

final sdk = FrappeSDK(
  baseUrl: 'http://10.0.2.2:8001',
  isOnlineOverride: () async => true, // dev-only
);

Production callers should leave isOnlineOverride null and rely on the platform probe.

Tests

183 sync-related tests pass unchanged on this branch (test/sync/, test/concurrency/sync_mutex_test.dart, test/services/sync_engine_builder_send_test.dart). No new tests added — the change is opt-in and the existing suite covers the default path.

`SyncService.isOnline()` directly probes `connectivity_plus` and accepts
only `mobile | wifi | ethernet`. On Android emulators with `adb reverse`
the link reads as `ConnectivityResult.none` and the gate refuses sync
even though HTTP traffic to the bench is fully functional. Today the
only escape is to bypass `pullSync`/`pullSyncMany` entirely from the
consumer app — costing delta pulls, resumable cursors, and the
in-built page lookahead that `_pullOneInternal` already implements.

Add an optional `isOnlineOverride` callback wired through
`FrappeSDK(...)` → `SyncService(...)` → `isOnline()`. When set, the
override replaces the `connectivity_plus` probe. When unset (the
default), behaviour is exactly as today — fully backwards-compatible.

Semantics:
- The `offlineMode.enabled` gate always runs first; overriding doesn't
  bypass an explicit offline-mode opt-out.
- If the override throws, `isOnline()` logs and falls through to the
  platform probe so a buggy callback can't brick sync.

Production callers should leave this null. Legitimate use is the
emulator+adb-reverse dev workflow, where the consumer sets
`isOnlineOverride: () async => true`. The existing private
`_isOnlineOverrideForTesting` test seam is unchanged.

Both `FrappeSDK()` and `FrappeSDK.forTesting()` accept the parameter;
the test constructor initialises it to null in its initializer list.
183 sync-related tests pass unchanged on this branch.
@Omprakash-48 Omprakash-48 self-requested a review June 2, 2026 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants