Guarantees¶
Scheduler.guarantees() returns a per-platform truth table. UX should branch on it rather than assume parity.
| Field | Android WorkManager |
iOS 18 BGTaskScheduler |
macOS 15 NSBackgroundActivityScheduler |
|---|---|---|---|
survivesProcessDeath |
true | true | true |
survivesReboot |
true | true | true |
survivesForceQuit |
true | false | true |
honoursWallClock |
approximate | false (hint only) | approximate |
supportsRetryBackoff |
true (native) | true (library-emulated) | true (library-emulated) |
cancelsInFlight |
true | false | true |
minimumPeriodicInterval |
15 min | 15 min recommended | 1 sec |
maxConcurrentTasks |
unbounded-ish | ~1000 | unbounded-ish |
Read carefully:
survivesForceQuit = falseon iOS. The single most important caveat. See Force-quit caveat (iOS).honoursWallClock = falseon iOS meansearliestBeginDateis a hint — the system can defer indefinitely based on opaque heuristics (battery state, usage patterns, Low Power Mode).cancelsInFlight = falseon iOS meansScheduler.cancel(taskId)only kills pending requests; a worker already executing on iOS finishes whatever it was doing.
Branching UX on guarantees¶
val g = scheduler.guarantees()
if (!g.survivesForceQuit) {
// iOS: educate the user.
showToast("Open the app daily so we can sync.")
}
if (!g.honoursWallClock) {
// iOS: don't promise a wall-clock cadence.
label.text = "Syncs throughout the day" // not "syncs every hour"
}
if (!g.cancelsInFlight) {
// iOS: a Cancel button only stops *future* runs, not the current one.
cancelButton.subtitle = "Stops future runs"
}
What's not in the table¶
isForeground— there's no notion of "foreground work" in v1 (AndroidsetForegroundlands asExecutionHint.LongRunningin v2).requiresChargingon Apple — Android honoursWorkConstraints.requiresChargingnatively via WorkManager. On iOS / macOS the library has no charging probe, so the field is silently ignored. Workers that need charging should check insideexecute()and returnWorkResult.Retry.- Anything observability-related —
Scheduler.observe()is v2.
Network constraints — honoured everywhere¶
WorkConstraints.networkRequired is honoured on every platform, by different mechanisms:
- Android —
WorkManagerrefuses to dispatch the worker until the constraint is met. The OS holds it indefinitely. - iOS / macOS — a library-managed pre-execution gate, driven by
reachable, waits up tomin(5 s, budget / 4). On timeout the worker is short-circuited toWorkResult.Retryand the scheduler reschedules per the request'sBackoffPolicy.
NetworkRequirement.Unmetered is honoured against ReachabilityStatus.isDataMetered == false (wifi/ethernet) on all platforms — Android maps to NetworkType.UNMETERED; Apple checks the metered axis directly. The legacy "downgrade Unmetered to Any on iOS" behaviour is gone. See Recipes → Require a network connection.