capacitor-mobilecron 0.2.17 → 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.
- package/README.md +54 -7
- package/ios/Plugin/MobileCronPlugin.m +4 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
268
|
+
### Android E2E tests (CDP)
|
|
247
269
|
|
|
248
|
-
Full integration suite against a running Android
|
|
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
|
-
|
|
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
|
|
|
@@ -10,4 +10,8 @@ CAP_PLUGIN(MobileCronPlugin, "MobileCron",
|
|
|
10
10
|
CAP_PLUGIN_METHOD(resumeAll, CAPPluginReturnPromise);
|
|
11
11
|
CAP_PLUGIN_METHOD(setMode, CAPPluginReturnPromise);
|
|
12
12
|
CAP_PLUGIN_METHOD(getStatus, CAPPluginReturnPromise);
|
|
13
|
+
CAP_PLUGIN_METHOD(testNativeEvaluate, CAPPluginReturnPromise);
|
|
14
|
+
CAP_PLUGIN_METHOD(testSetNextDueAt, CAPPluginReturnPromise);
|
|
15
|
+
CAP_PLUGIN_METHOD(testInjectPendingEvent, CAPPluginReturnPromise);
|
|
16
|
+
CAP_PLUGIN_METHOD(testGetPendingCount, CAPPluginReturnPromise);
|
|
13
17
|
)
|
package/package.json
CHANGED