@symbiote-native/task-manager 0.1.2 → 0.1.3
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 +47 -29
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# @symbiote-native/task-manager
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
Run your code when the OS wakes the app in the background: a location update, a geofence event, a
|
|
4
|
+
periodic sync, a silent push. One API for every [SymbioteNative](../../README.md) adapter (React,
|
|
5
|
+
Vue, Svelte, Solid and Angular).
|
|
6
|
+
|
|
7
|
+
It wraps [`expo-task-manager`](https://github.com/expo/expo/tree/main/packages/expo-task-manager).
|
|
8
|
+
Like [`@symbiote-native/local-auth`](../local-auth), every export is a plain function or a one-time
|
|
9
|
+
module-load side effect, so there is no hook, composable or service to wrap: every adapter entry
|
|
10
|
+
point is a plain re-export of the same `core`.
|
|
9
11
|
|
|
10
12
|
This package is the low-level primitive other background-work packages register tasks through
|
|
11
|
-
(background location, geofencing, background notification delivery)
|
|
12
|
-
schedule anything
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
module.
|
|
13
|
+
(background location, geofencing, background notification delivery). It does **not** itself
|
|
14
|
+
schedule anything: it defines tasks, tracks which are registered, dispatches native's task-execute
|
|
15
|
+
event to the matching executor, and acks completion. Starting a task running (periodic scheduling,
|
|
16
|
+
geofence triggers) is each consumer's own job, done through its own native module.
|
|
16
17
|
|
|
17
18
|
## Install
|
|
18
19
|
|
|
@@ -29,31 +30,31 @@ npx @symbiote-native/cli add --task-manager
|
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
Either way: installs `@symbiote-native/task-manager` and wires the native autolinking
|
|
32
|
-
automatically
|
|
33
|
+
automatically - see [`@symbiote-native/cli`](../cli). Most apps get this transitively anyway, pulled in by
|
|
33
34
|
`--location`/`--audio`/`--background-fetch`/`--background-task`/`--notifications`.
|
|
34
35
|
|
|
35
36
|
<details>
|
|
36
|
-
<summary>Manual install (no CLI
|
|
37
|
+
<summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
|
|
37
38
|
|
|
38
39
|
```bash
|
|
39
40
|
npm install @symbiote-native/task-manager
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
`expo-task-manager`, `unimodules-app-loader`, and `expo-modules-core` come along as regular
|
|
43
|
-
dependencies, pinned to exact versions
|
|
44
|
+
dependencies, pinned to exact versions - never install them yourself, and never add the `expo`
|
|
44
45
|
meta-package to your project.
|
|
45
46
|
|
|
46
47
|
## Required one-time step: native autolinking wiring
|
|
47
48
|
|
|
48
|
-
Same one-time step as every other `expo-modules-core` package this project ships
|
|
49
|
+
Same one-time step as every other `expo-modules-core` package this project ships - see
|
|
49
50
|
[`@symbiote-native/local-auth`'s README](../local-auth/README.md#required-one-time-step-native-autolinking-wiring)
|
|
50
51
|
and the `symbiote-expo-native-module` project skill. Android's headless-boot loader
|
|
51
52
|
(`RNHeadlessAppLoader`) and iOS's `TaskManagerAppDelegateSubscriber` are both discovered
|
|
52
|
-
automatically by Expo's own autolinking the moment this package is installed
|
|
53
|
+
automatically by Expo's own autolinking the moment this package is installed - no extra
|
|
53
54
|
`native-link.json` entry, no app-level edit.
|
|
54
55
|
|
|
55
56
|
One iOS `Info.plist` key IS wired: `UIBackgroundModes: fetch`
|
|
56
|
-
(`native-link.json`'s `ios.infoPlistArrayKeys`)
|
|
57
|
+
(`native-link.json`'s `ios.infoPlistArrayKeys`) - upstream's own `withTaskManager.ts` config
|
|
57
58
|
plugin adds this unconditionally, as the baseline mode any registered task's background delivery
|
|
58
59
|
relies on, independent of which consumer package (`background-fetch`, `location`, …) actually
|
|
59
60
|
registers a task. `@symbiote-native/expo-modules-link` merges it into the same array those
|
|
@@ -68,8 +69,8 @@ src/core/ defineTask / isTaskDefined / isTaskRegisteredAsync / getTaskOption
|
|
|
68
69
|
getRegisteredTasksAsync / unregisterTaskAsync / unregisterAllTasksAsync /
|
|
69
70
|
isAvailableAsync, plus the task-body/executor types. native-module.ts resolves the
|
|
70
71
|
native module via expo-modules-core's requireNativeModule and wires one listener at
|
|
71
|
-
module load
|
|
72
|
-
src/angular/ @symbiote-native/task-manager/angular
|
|
72
|
+
module load - native invokes it whenever a defined task should run.
|
|
73
|
+
src/angular/ @symbiote-native/task-manager/angular - export * from '../core'
|
|
73
74
|
```
|
|
74
75
|
|
|
75
76
|
`./react`, `./vue`, `./svelte`, and `./solid` are `exports`-map aliases straight onto
|
|
@@ -78,12 +79,12 @@ src/angular/ @symbiote-native/task-manager/angular — export * from '../core'
|
|
|
78
79
|
|
|
79
80
|
## Use it
|
|
80
81
|
|
|
81
|
-
`defineTask` must run at the top of the JS bundle, outside any component
|
|
82
|
+
`defineTask` must run at the top of the JS bundle, outside any component - the app can be
|
|
82
83
|
launched headlessly to run a background task, with no views mounted, so a task defined inside a
|
|
83
84
|
component lifecycle method would simply never register on that launch.
|
|
84
85
|
|
|
85
86
|
```ts
|
|
86
|
-
// index.ts, alongside AppRegistry.registerComponent
|
|
87
|
+
// index.ts, alongside AppRegistry.registerComponent - identical on every adapter
|
|
87
88
|
import { defineTask } from '@symbiote-native/task-manager';
|
|
88
89
|
|
|
89
90
|
const SYNC_TASK = 'background-sync';
|
|
@@ -97,7 +98,7 @@ defineTask(SYNC_TASK, async ({ data, error }) => {
|
|
|
97
98
|
});
|
|
98
99
|
```
|
|
99
100
|
|
|
100
|
-
A consumer registers the task with native through its own native module
|
|
101
|
+
A consumer registers the task with native through its own native module -
|
|
101
102
|
[`@symbiote-native/location`](../location)'s `startLocationUpdatesAsync`/`startGeofencingAsync`,
|
|
102
103
|
[`@symbiote-native/background-fetch`](../background-fetch)'s `registerTaskAsync`,
|
|
103
104
|
[`@symbiote-native/background-task`](../background-task)'s `registerTaskAsync`, or
|
|
@@ -116,7 +117,7 @@ const tasks = await getRegisteredTasksAsync();
|
|
|
116
117
|
await unregisterTaskAsync(SYNC_TASK); // stop receiving updates for this task
|
|
117
118
|
```
|
|
118
119
|
|
|
119
|
-
Identical import surface on every adapter
|
|
120
|
+
Identical import surface on every adapter - `@symbiote-native/task-manager/react`,
|
|
120
121
|
`/vue`, `/svelte`, `/solid`, `/angular` all re-export the same functions.
|
|
121
122
|
|
|
122
123
|
## API
|
|
@@ -133,16 +134,16 @@ isAvailableAsync(): Promise<boolean>
|
|
|
133
134
|
```
|
|
134
135
|
|
|
135
136
|
Plus `ITaskManagerError`, `ITaskManagerTask`, `ITaskManagerTaskBody`,
|
|
136
|
-
`ITaskManagerTaskBodyExecutionInfo`, `ITaskManagerTaskExecutor`
|
|
137
|
+
`ITaskManagerTaskBodyExecutionInfo`, `ITaskManagerTaskExecutor` - ported from upstream's
|
|
137
138
|
`TaskManager.ts`, renamed with this repo's `I`-prefix convention for exported types
|
|
138
|
-
(`ts-js-best-practices`). Unlike upstream, there is no `registerTaskAsync` free function here
|
|
139
|
+
(`ts-js-best-practices`). Unlike upstream, there is no `registerTaskAsync` free function here -
|
|
139
140
|
upstream never exposes one either; registration is always driven by the consumer that needs the
|
|
140
141
|
task (`@symbiote-native/location`, `@symbiote-native/background-fetch`,
|
|
141
142
|
`@symbiote-native/background-task`, `@symbiote-native/notifications`).
|
|
142
143
|
|
|
143
144
|
## Notes
|
|
144
145
|
|
|
145
|
-
- **`isAvailableAsync` resolves `false` rather than throwing when the native method is missing**
|
|
146
|
+
- **`isAvailableAsync` resolves `false` rather than throwing when the native method is missing** -
|
|
146
147
|
every other guarded function throws an `UnavailabilityError`. This matches upstream's own
|
|
147
148
|
contract: "can this API be used at all" has to answer even where the rest of the surface can't.
|
|
148
149
|
- **A defined task that native fires but nobody registered still gets acked and unregistered.**
|
|
@@ -153,12 +154,29 @@ task (`@symbiote-native/location`, `@symbiote-native/background-fetch`,
|
|
|
153
154
|
`notifyTaskFinishedAsync` always fires in a `finally`, so a bug in one task's executor can't
|
|
154
155
|
leave the OS believing the task never completed.
|
|
155
156
|
|
|
157
|
+
## Common questions
|
|
158
|
+
|
|
159
|
+
- **My task never runs.** Run `defineTask` at the top of the bundle, outside any component, before
|
|
160
|
+
anything registers it (registering an undefined task throws); and make sure a consumer package
|
|
161
|
+
registered it, since `defineTask` schedules nothing.
|
|
162
|
+
- **Stops when the app is swiped away.** iOS terminates it; on Android recents removal varies by
|
|
163
|
+
vendor.
|
|
164
|
+
- **Stops after 5 to 10 minutes on Android.** Doze mode: use a foreground service for continuous work.
|
|
165
|
+
- **Works in debug, not release.** Test release builds early on Android.
|
|
166
|
+
- **Simulators.** Use a physical device; the iOS Simulator has no background task scheduler.
|
|
167
|
+
|
|
168
|
+
Sources: [Expo docs: BackgroundTask](https://docs.expo.dev/versions/latest/sdk/background-task/),
|
|
169
|
+
[expo/expo#9570](https://github.com/expo/expo/issues/9570),
|
|
170
|
+
[expo/expo#3535](https://github.com/expo/expo/issues/3535),
|
|
171
|
+
[expo/expo#14076](https://github.com/expo/expo/issues/14076),
|
|
172
|
+
[expo/expo#26717](https://github.com/expo/expo/issues/26717).
|
|
173
|
+
|
|
156
174
|
## Test it
|
|
157
175
|
|
|
158
|
-
No Fabric/Descriptor angle at all
|
|
176
|
+
No Fabric/Descriptor angle at all - every function here is a pure async-function surface plus one
|
|
159
177
|
module-load event listener, never a view or per-instance state. Tests inject a fake native-module
|
|
160
178
|
object in place of the real `requireNativeModule` resolution and fire the wired listener directly
|
|
161
|
-
(`src/core/task-manager.test.ts`)
|
|
179
|
+
(`src/core/task-manager.test.ts`) - no `installFabric()`, no ViewConfig. The headless-relaunch
|
|
162
180
|
mechanics themselves (native tearing down and re-executing the whole JS bundle with no UI mounted)
|
|
163
|
-
are OS-level and can only be verified on a real device
|
|
181
|
+
are OS-level and can only be verified on a real device - see the `symbiote-expo-native-module`
|
|
164
182
|
skill §10e for what's traced from source versus still unverified.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/task-manager",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "expo-task-manager wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Defines and tracks background tasks that native code invokes headlessly.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"expo-task-manager": "57.0.15",
|
|
73
73
|
"unimodules-app-loader": "57.0.1",
|
|
74
74
|
"expo-constants": "57.0.19",
|
|
75
|
-
"expo-modules-core": "57.0.
|
|
75
|
+
"expo-modules-core": "57.0.20"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
78
|
"@angular/core": ">=20",
|
|
@@ -82,12 +82,12 @@
|
|
|
82
82
|
"solid-js": ">=1.9.0",
|
|
83
83
|
"svelte": ">=5.56.0",
|
|
84
84
|
"vue": ">=3.5.0",
|
|
85
|
-
"@symbiote-native/angular": "^3.
|
|
86
|
-
"@symbiote-native/engine": "^1.
|
|
87
|
-
"@symbiote-native/react": "^3.0
|
|
88
|
-
"@symbiote-native/solid": "^3.0
|
|
89
|
-
"@symbiote-native/svelte": "^3.0
|
|
90
|
-
"@symbiote-native/vue": "^3.0
|
|
85
|
+
"@symbiote-native/angular": "^3.2.0",
|
|
86
|
+
"@symbiote-native/engine": "^1.5.0",
|
|
87
|
+
"@symbiote-native/react": "^3.2.0",
|
|
88
|
+
"@symbiote-native/solid": "^3.1.0",
|
|
89
|
+
"@symbiote-native/svelte": "^3.1.0",
|
|
90
|
+
"@symbiote-native/vue": "^3.2.0"
|
|
91
91
|
},
|
|
92
92
|
"peerDependenciesMeta": {
|
|
93
93
|
"@symbiote-native/angular": {
|
|
@@ -135,13 +135,13 @@
|
|
|
135
135
|
"solid-js": "^1.9.14",
|
|
136
136
|
"svelte": "^5.56.0",
|
|
137
137
|
"typescript": "~6.0.0",
|
|
138
|
-
"@symbiote-native/angular": "3.
|
|
139
|
-
"@symbiote-native/engine": "1.
|
|
140
|
-
"@symbiote-native/react": "3.0
|
|
141
|
-
"@symbiote-native/solid": "3.0
|
|
142
|
-
"@symbiote-native/svelte": "3.0
|
|
143
|
-
"@symbiote-native/test-utils": "0.4.
|
|
144
|
-
"@symbiote-native/vue": "3.0
|
|
138
|
+
"@symbiote-native/angular": "3.2.0",
|
|
139
|
+
"@symbiote-native/engine": "1.5.0",
|
|
140
|
+
"@symbiote-native/react": "3.2.0",
|
|
141
|
+
"@symbiote-native/solid": "3.1.0",
|
|
142
|
+
"@symbiote-native/svelte": "3.1.0",
|
|
143
|
+
"@symbiote-native/test-utils": "0.4.6",
|
|
144
|
+
"@symbiote-native/vue": "3.2.0"
|
|
145
145
|
},
|
|
146
146
|
"scripts": {
|
|
147
147
|
"typecheck": "tsc --build",
|