capacitor-mobilecron 0.2.18 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +54 -7
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -193,17 +193,39 @@ MobileCron.addListener('jobSkipped', ({ id, name, reason }) => {
193
193
 
194
194
  ## How native background execution works
195
195
 
196
- On Android, `capacitor-mobilecron` registers a **WorkManager** periodic task (every 15 min in `balanced` mode). When it fires — even while the WebView is suspended:
196
+ ### Android
197
+
198
+ `capacitor-mobilecron` registers a **WorkManager** periodic task (every 15 min in `balanced` mode). When it fires — even while the WebView is suspended:
197
199
 
198
200
  1. `NativeJobEvaluator` reads job state from `CapacitorStorage` (SharedPreferences)
199
- 2. It evaluates which jobs are due, checks active-hour windows and constraints
201
+ 2. It evaluates which jobs are due, checks active-hour windows and constraints (`requiresNetwork` via `ConnectivityManager`, `requiresCharging` via `BatteryManager`)
200
202
  3. Fired events are written to `pendingNativeEvents` in storage
201
203
  4. `CronBridge` wakes the plugin — if the WebView is alive, `jobDue` events are dispatched immediately
202
204
  5. On next app foreground (`handleOnResume`), any remaining pending events are dispatched
203
205
 
204
206
  A **ChargingReceiver** also triggers evaluation when the device starts charging.
205
207
 
206
- On iOS, `BGAppRefreshTask` and `BGProcessingTask` follow the same pattern via `NativeJobEvaluator.swift`.
208
+ ### iOS
209
+
210
+ `BGAppRefreshTask` and `BGProcessingTask` follow the same pattern via `NativeJobEvaluator.swift`. State is persisted to a JSON file in Application Support (with UserDefaults as fallback), ensuring it survives both `SIGKILL` and normal app termination.
211
+
212
+ ### Platform differences and tradeoffs
213
+
214
+ | Behavior | Android | iOS |
215
+ |----------|---------|-----|
216
+ | **Background wake frequency** | WorkManager ~15 min (`balanced`), ~5 min chain (`aggressive`) | `BGAppRefreshTask` — iOS decides timing (typically 15–30 min, but system-managed) |
217
+ | **`requiresNetwork` constraint** | Checked by `NativeJobEvaluator` at evaluation time via `ConnectivityManager` | **Not checked** at evaluation time — enforced at the `BGProcessingTask` constraint level instead (`requiresNetworkConnectivity`) |
218
+ | **`requiresCharging` constraint** | Checked by `NativeJobEvaluator` at evaluation time via `BatteryManager` sticky intent | **Not checked** at evaluation time — enforced at the `BGProcessingTask` constraint level instead (`requiresExternalPower`) |
219
+ | **Charging event trigger** | `ChargingReceiver` fires evaluation immediately on plug-in | No equivalent — relies on next scheduled `BGTask` |
220
+ | **State persistence** | SharedPreferences (`CAPStorage`) | JSON file in Application Support + UserDefaults dual-write (survives SIGKILL) |
221
+ | **Minimum interval** | 15 min (WorkManager enforced) | 15 min (`earliestBeginDate`), actual timing system-managed |
222
+ | **`aggressive` mode** | 5-min one-shot chain worker — reliable sub-15-min execution | Maps to `BGProcessingTask` without `requiresExternalPower` — still system-scheduled |
223
+
224
+ **Key tradeoff on iOS constraints:** Android's `NativeJobEvaluator` checks `requiresNetwork` and `requiresCharging` at the moment a job is evaluated and will skip (increment `consecutiveSkips`) if constraints aren't met. On iOS, these constraints are delegated to `BGTaskScheduler` — iOS simply won't launch the background task until the constraints are satisfied. This means:
225
+
226
+ - On Android, a job can be skipped (and the skip is visible via `consecutiveSkips` / `jobSkipped` event) when constraints aren't met during a background wake.
227
+ - On iOS, the background task itself is delayed until constraints are met, so the job fires when it eventually runs — no skip event is emitted for constraint violations.
228
+ - `activeHours` and `paused` state are checked on **both** platforms by `NativeJobEvaluator` at evaluation time.
207
229
 
208
230
  ## Advanced: `MobileCronScheduler`
209
231
 
@@ -243,9 +265,9 @@ npm run test:coverage # coverage report
243
265
 
244
266
  Tests cover schedule computation, active-hour windows, persistence, pause/resume, skip logic, native event rehydration, and more.
245
267
 
246
- ### Device E2E tests (Android, via CDP)
268
+ ### Android E2E tests (CDP)
247
269
 
248
- Full integration suite against a running Android app — 8 sections, 50 tests:
270
+ Full integration suite against a running Android device via Chrome DevTools Protocol — 8 sections, 50 tests:
249
271
 
250
272
  ```bash
251
273
  # 1. Build and install the example app
@@ -259,9 +281,34 @@ adb shell am start -n io.mobilecron.test/.MainActivity
259
281
  npm run test:e2e
260
282
  ```
261
283
 
262
- The suite covers stress tests, background/foreground cycles, event reliability, edge cases, mode switching, real-world scenarios, native wake, and **native background execution** (WorkManager evaluation, `pendingNativeEvents` rehydration, skip logic, dedup).
284
+ ### iOS E2E tests (Simulator)
285
+
286
+ Full integration suite against the iOS Simulator — 8 sections, 50 tests:
287
+
288
+ ```bash
289
+ # 1. Boot a simulator
290
+ xcrun simctl boot "iPhone 16e"
291
+
292
+ # 2. From the project root, run the suite (builds, installs, launches automatically)
293
+ npm run test:e2e:ios
294
+ ```
295
+
296
+ The iOS suite uses a multi-phase architecture with kill+relaunch cycles to test state persistence across process termination, then runs native background evaluation tests (NativeJobEvaluator, pendingNativeEvents, skip logic, dedup) inline.
297
+
298
+ ### What the E2E suites cover
299
+
300
+ Both Android and iOS suites validate the same 8 sections:
301
+
302
+ 1. **Stress** — rapid register/unregister/concurrent operations (50+ jobs)
303
+ 2. **State persistence** — jobs, paused state, and mode survive kill+relaunch cycles
304
+ 3. **Events** — `jobDue`, `statusChanged`, listener add/remove, rapid-fire delivery
305
+ 4. **Edge cases** — empty names, missing IDs, large payloads, special characters, idempotent operations
306
+ 5. **Mode switching** — eco/balanced/aggressive transitions with active jobs
307
+ 6. **Real-world scenarios** — hourly jobs, one-shot schedules, full lifecycle, pause+trigger interactions
308
+ 7. **Diagnostics** — platform-specific status fields, `nativeWake` event, BGTask registration (iOS) / WorkManager status (Android)
309
+ 8. **Native background** — `NativeJobEvaluator` fires due jobs, `pendingNativeEvents` rehydration, skip-when-paused, `consecutiveSkips` tracking, `nextDueAt` dedup
263
310
 
264
- See [`tests/e2e/test-e2e.mjs`](tests/e2e/test-e2e.mjs) and [`example/`](example/) for details.
311
+ See [`tests/e2e/test-e2e.mjs`](tests/e2e/test-e2e.mjs) (Android), [`tests/e2e/test-e2e-ios.mjs`](tests/e2e/test-e2e-ios.mjs) (iOS), and [`example/`](example/) for details.
265
312
 
266
313
  ## Contributing
267
314
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-mobilecron",
3
- "version": "0.2.18",
3
+ "version": "0.2.19",
4
4
  "description": "Capacitor scheduling primitive that emits job due events across web, Android, and iOS",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",