@symbiote-native/background-fetch 0.1.0
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/LICENSE +21 -0
- package/README.md +161 -0
- package/build/angular/index.d.ts +1 -0
- package/build/angular/index.js +4 -0
- package/build/core/background-fetch.d.ts +18 -0
- package/build/core/background-fetch.js +70 -0
- package/build/core/index.d.ts +3 -0
- package/build/core/index.js +2 -0
- package/build/core/native-module.d.ts +8 -0
- package/build/core/native-module.js +3 -0
- package/build/core/types.d.ts +47 -0
- package/build/core/types.js +26 -0
- package/build-ngc/angular/index.d.ts +1 -0
- package/build-ngc/angular/index.js +5 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/core/background-fetch.d.ts +18 -0
- package/build-ngc/core/background-fetch.js +71 -0
- package/build-ngc/core/background-fetch.js.map +1 -0
- package/build-ngc/core/index.d.ts +3 -0
- package/build-ngc/core/index.js +3 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +8 -0
- package/build-ngc/core/native-module.js +4 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/types.d.ts +47 -0
- package/build-ngc/core/types.js +27 -0
- package/build-ngc/core/types.js.map +1 -0
- package/native-link.json +15 -0
- package/package.json +151 -0
- package/src/angular/index.ts +4 -0
- package/src/core/background-fetch.ts +86 -0
- package/src/core/index.ts +8 -0
- package/src/core/native-module.ts +22 -0
- package/src/core/types.ts +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 A. Prokopenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# @symbiote-native/background-fetch
|
|
2
|
+
|
|
3
|
+
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
|
+
[`expo-background-fetch`](https://github.com/expo/expo/tree/main/packages/expo-background-fetch)
|
|
5
|
+
usable from **every** adapter — React, Vue, Svelte, Solid, and Angular. Like
|
|
6
|
+
[`@symbiote-native/task-manager`](../task-manager), every export is a plain async function, so
|
|
7
|
+
there is no hook/composable/service to wrap: the React, Vue, Svelte, Solid, and Angular entry
|
|
8
|
+
points are plain re-exports of the same `core`.
|
|
9
|
+
|
|
10
|
+
**Upstream deprecated this API in favor of `expo-background-task`** — Apple and Google are both
|
|
11
|
+
moving away from periodic-fetch-style scheduling toward task-scheduling APIs
|
|
12
|
+
(`BGTaskScheduler` / `WorkManager`). It is ported here anyway, alongside
|
|
13
|
+
[`@symbiote-native/background-task`](../background-task), because Expo still ships both
|
|
14
|
+
simultaneously as of sdk-57 and an app already built against the old API needs a path to run on
|
|
15
|
+
SymbioteNative too. Prefer `@symbiote-native/background-task` for new code.
|
|
16
|
+
|
|
17
|
+
This package registers a task with native so it fires **periodically in the background** — it
|
|
18
|
+
does not define what the task does. Define the task first via
|
|
19
|
+
[`@symbiote-native/task-manager`](../task-manager)'s `defineTask`, then register it for periodic
|
|
20
|
+
execution via this package's `registerTaskAsync`.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
**New app:**
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx @symbiote-native/cli new my-app --background-fetch
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Existing SymbioteNative app:**
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx @symbiote-native/cli add --background-fetch
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Either way: installs `@symbiote-native/background-fetch` + `@symbiote-native/task-manager` and
|
|
37
|
+
wires the native autolinking automatically — see [`@symbiote-native/cli`](../cli).
|
|
38
|
+
|
|
39
|
+
<details>
|
|
40
|
+
<summary>Manual install (no CLI — installing and wiring native autolinking by hand)</summary>
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install @symbiote-native/background-fetch @symbiote-native/task-manager
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`expo-background-fetch` and `expo-modules-core` come along as regular dependencies, pinned to
|
|
47
|
+
exact versions — never install them yourself, and never add the `expo` meta-package to your
|
|
48
|
+
project.
|
|
49
|
+
|
|
50
|
+
## Required one-time step: native autolinking wiring
|
|
51
|
+
|
|
52
|
+
Same one-time step as every other `expo-modules-core` package this project ships — see
|
|
53
|
+
[`@symbiote-native/local-auth`'s README](../local-auth/README.md#required-one-time-step-native-autolinking-wiring)
|
|
54
|
+
and the `symbiote-expo-native-module` project skill.
|
|
55
|
+
|
|
56
|
+
iOS also needs `UIBackgroundModes: fetch` in the app's Info.plist —
|
|
57
|
+
`native-link.json`'s `ios.infoPlistArrayKeys` covers this ARRAY-valued case (see
|
|
58
|
+
`@symbiote-native/expo-modules-link`), so it's wired automatically by the same postinstall step,
|
|
59
|
+
no manual edit needed. Android needs no manual step — `RECEIVE_BOOT_COMPLETED` and `WAKE_LOCK`
|
|
60
|
+
ship in `expo-background-fetch`'s own `AndroidManifest.xml` and merge automatically once the
|
|
61
|
+
package is installed.
|
|
62
|
+
|
|
63
|
+
</details>
|
|
64
|
+
|
|
65
|
+
## Shape
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
src/core/ getStatusAsync / setMinimumIntervalAsync / registerTaskAsync / unregisterTaskAsync,
|
|
69
|
+
plus BackgroundFetchResult / BackgroundFetchStatus / IBackgroundFetchOptions.
|
|
70
|
+
native-module.ts resolves the native module via expo-modules-core's
|
|
71
|
+
requireNativeModule.
|
|
72
|
+
src/angular/ @symbiote-native/background-fetch/angular — export * from '../core'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`./react`, `./vue`, `./svelte`, and `./solid` are `exports`-map aliases straight onto
|
|
76
|
+
`src/core/`. `./angular` stays a physical file/subpath since Angular ships through a separate
|
|
77
|
+
`ngc`/AOT build (`build-ngc/`).
|
|
78
|
+
|
|
79
|
+
## Use it
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// index.ts, alongside AppRegistry.registerComponent — identical on every adapter
|
|
83
|
+
import { defineTask } from '@symbiote-native/task-manager';
|
|
84
|
+
import {
|
|
85
|
+
registerTaskAsync,
|
|
86
|
+
BackgroundFetchResult,
|
|
87
|
+
} from '@symbiote-native/background-fetch';
|
|
88
|
+
|
|
89
|
+
const SYNC_TASK = 'background-sync';
|
|
90
|
+
|
|
91
|
+
defineTask(SYNC_TASK, async ({ data, error }) => {
|
|
92
|
+
if (error) {
|
|
93
|
+
console.error('background-sync failed:', error);
|
|
94
|
+
return BackgroundFetchResult.Failed;
|
|
95
|
+
}
|
|
96
|
+
const receivedNewData = await runSync(data);
|
|
97
|
+
return receivedNewData
|
|
98
|
+
? BackgroundFetchResult.NewData
|
|
99
|
+
: BackgroundFetchResult.NoData;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Somewhere after the task is defined (a settings screen, app bootstrap, …):
|
|
103
|
+
await registerTaskAsync(SYNC_TASK, { minimumInterval: 900 });
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`@symbiote-native/task-manager` is the primitive both this package and
|
|
107
|
+
[`@symbiote-native/background-task`](../background-task) build on — `defineTask` lives there,
|
|
108
|
+
`registerTaskAsync`/`unregisterTaskAsync` live here. `defineTask` must run at the top of the JS
|
|
109
|
+
bundle, outside any component, for the same reason documented in task-manager's own README: the
|
|
110
|
+
app can be launched headlessly to run a background task, with no views mounted.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import {
|
|
114
|
+
getStatusAsync,
|
|
115
|
+
unregisterTaskAsync,
|
|
116
|
+
} from '@symbiote-native/background-fetch';
|
|
117
|
+
|
|
118
|
+
const status = await getStatusAsync();
|
|
119
|
+
await unregisterTaskAsync(SYNC_TASK); // stop receiving background-fetch callbacks for it
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Identical import surface on every adapter — `@symbiote-native/background-fetch/react`,
|
|
123
|
+
`/vue`, `/svelte`, `/solid`, `/angular` all re-export the same functions.
|
|
124
|
+
|
|
125
|
+
## API
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
getStatusAsync(): Promise<BackgroundFetchStatus | null>
|
|
129
|
+
setMinimumIntervalAsync(minimumInterval: number): Promise<void>
|
|
130
|
+
registerTaskAsync(taskName: string, options?: IBackgroundFetchOptions): Promise<void>
|
|
131
|
+
unregisterTaskAsync(taskName: string): Promise<void>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Plus `BackgroundFetchResult`, `BackgroundFetchStatus`, `IBackgroundFetchOptions` — ported from
|
|
135
|
+
upstream's `BackgroundFetch.types.ts`, the options type renamed with this repo's `I`-prefix
|
|
136
|
+
convention for exported types (`ts-js-best-practices`).
|
|
137
|
+
|
|
138
|
+
## Notes
|
|
139
|
+
|
|
140
|
+
- **Every function warns once, on first call, that this API is deprecated** — matching upstream's
|
|
141
|
+
own `showDeprecationWarning`. It still works; the warning is a nudge toward
|
|
142
|
+
`@symbiote-native/background-task`, not a functional restriction.
|
|
143
|
+
- **`registerTaskAsync` requires the task to already be defined.** It throws if
|
|
144
|
+
`@symbiote-native/task-manager`'s `isTaskDefined(taskName)` is `false` — call `defineTask`
|
|
145
|
+
first.
|
|
146
|
+
- **`getStatusAsync` shortcuts to `Available` on Android without calling native at all** —
|
|
147
|
+
matches upstream, which has no Android-side status concept (the native call exists only on
|
|
148
|
+
iOS).
|
|
149
|
+
- **`setMinimumIntervalAsync` silently no-ops when native lacks the method** (Android has no
|
|
150
|
+
equivalent call) rather than throwing — matches upstream.
|
|
151
|
+
- **Expo Go is out of scope.** Upstream also warns when running inside Expo Go
|
|
152
|
+
(`isRunningInExpoGo`, imported from the `expo` meta-package). This project never installs
|
|
153
|
+
`expo` — every app here is a bare/dev-client build, never Expo Go — so that check has no
|
|
154
|
+
equivalent here and is intentionally not ported.
|
|
155
|
+
|
|
156
|
+
## Test it
|
|
157
|
+
|
|
158
|
+
No Fabric/Descriptor angle at all — every function here is a pure async-function surface, never a
|
|
159
|
+
view or per-instance state. Tests inject a fake native-module object in place of the real
|
|
160
|
+
`requireNativeModule` resolution and a fake `@symbiote-native/task-manager` module
|
|
161
|
+
(`src/core/background-fetch.test.ts`) — no `installFabric()`, no ViewConfig.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../core';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// @symbiote-native/background-fetch/angular: the Angular entry over the framework-agnostic core.
|
|
2
|
+
// Same reasoning as the React/Vue entries — no per-instance state or event stream to wrap in a
|
|
3
|
+
// service, so this is a plain re-export.
|
|
4
|
+
export * from '../core/index.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BackgroundFetchStatus } from './types';
|
|
2
|
+
import type { IBackgroundFetchOptions } from './types';
|
|
3
|
+
/** Gets the current background-fetch status, or `null` if the native module is unreachable. */
|
|
4
|
+
export declare function getStatusAsync(): Promise<BackgroundFetchStatus | null>;
|
|
5
|
+
/**
|
|
6
|
+
* Sets the minimum number of seconds that must elapse before another background fetch can be
|
|
7
|
+
* initiated. Advisory only — iOS treats it as a floor, not an exact schedule. No effect on
|
|
8
|
+
* Android.
|
|
9
|
+
*/
|
|
10
|
+
export declare function setMinimumIntervalAsync(minimumInterval: number): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Registers a background-fetch task with the given name. The task must already be defined via
|
|
13
|
+
* `@symbiote-native/task-manager`'s `defineTask` — registration is driven by this package, but
|
|
14
|
+
* execution dispatch is task-manager's job.
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerTaskAsync(taskName: string, options?: IBackgroundFetchOptions): Promise<void>;
|
|
17
|
+
/** Unregisters a background-fetch task, so the app stops receiving fetch callbacks for it. */
|
|
18
|
+
export declare function unregisterTaskAsync(taskName: string): Promise<void>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Platform, UnavailabilityError } from 'expo-modules-core';
|
|
2
|
+
import { isTaskDefined } from '@symbiote-native/task-manager';
|
|
3
|
+
import { expoBackgroundFetch } from './native-module.js';
|
|
4
|
+
import { BackgroundFetchStatus } from './types.js';
|
|
5
|
+
const NATIVE_MODULE_NAME = 'BackgroundFetch';
|
|
6
|
+
// expo-background-fetch is upstream-deprecated in favor of expo-background-task (still shipped
|
|
7
|
+
// in sdk-57 alongside it, same as this project's file-system legacy+modern split) — carry the
|
|
8
|
+
// same one-time console warning upstream shows, so an app that adopted the old API notices.
|
|
9
|
+
let didShowDeprecationWarning = false;
|
|
10
|
+
function warnDeprecated() {
|
|
11
|
+
if (didShowDeprecationWarning)
|
|
12
|
+
return;
|
|
13
|
+
didShowDeprecationWarning = true;
|
|
14
|
+
console.warn('@symbiote-native/background-fetch: this API is deprecated. Use @symbiote-native/background-task instead.');
|
|
15
|
+
}
|
|
16
|
+
function assertValidTaskName(taskName) {
|
|
17
|
+
if (!taskName || typeof taskName !== 'string') {
|
|
18
|
+
throw new TypeError('`taskName` must be a non-empty string.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Upstream also warns when running inside Expo Go (`isRunningInExpoGo`, imported from the `expo`
|
|
22
|
+
// meta-package). This project never installs `expo` — every app here is a bare/dev-client build,
|
|
23
|
+
// never Expo Go — so that check has no equivalent and is intentionally not ported.
|
|
24
|
+
/** Gets the current background-fetch status, or `null` if the native module is unreachable. */
|
|
25
|
+
export async function getStatusAsync() {
|
|
26
|
+
warnDeprecated();
|
|
27
|
+
if (Platform.OS === 'android') {
|
|
28
|
+
return BackgroundFetchStatus.Available;
|
|
29
|
+
}
|
|
30
|
+
if (!expoBackgroundFetch.getStatusAsync) {
|
|
31
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getStatusAsync');
|
|
32
|
+
}
|
|
33
|
+
return expoBackgroundFetch.getStatusAsync();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Sets the minimum number of seconds that must elapse before another background fetch can be
|
|
37
|
+
* initiated. Advisory only — iOS treats it as a floor, not an exact schedule. No effect on
|
|
38
|
+
* Android.
|
|
39
|
+
*/
|
|
40
|
+
export async function setMinimumIntervalAsync(minimumInterval) {
|
|
41
|
+
warnDeprecated();
|
|
42
|
+
if (!expoBackgroundFetch.setMinimumIntervalAsync)
|
|
43
|
+
return;
|
|
44
|
+
await expoBackgroundFetch.setMinimumIntervalAsync(minimumInterval);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Registers a background-fetch task with the given name. The task must already be defined via
|
|
48
|
+
* `@symbiote-native/task-manager`'s `defineTask` — registration is driven by this package, but
|
|
49
|
+
* execution dispatch is task-manager's job.
|
|
50
|
+
*/
|
|
51
|
+
export async function registerTaskAsync(taskName, options = {}) {
|
|
52
|
+
warnDeprecated();
|
|
53
|
+
if (!expoBackgroundFetch.registerTaskAsync) {
|
|
54
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'registerTaskAsync');
|
|
55
|
+
}
|
|
56
|
+
if (!isTaskDefined(taskName)) {
|
|
57
|
+
throw new Error(`Task '${taskName}' is not defined. You must define a task using defineTask (from @symbiote-native/task-manager) before registering.`);
|
|
58
|
+
}
|
|
59
|
+
assertValidTaskName(taskName);
|
|
60
|
+
await expoBackgroundFetch.registerTaskAsync(taskName, options);
|
|
61
|
+
}
|
|
62
|
+
/** Unregisters a background-fetch task, so the app stops receiving fetch callbacks for it. */
|
|
63
|
+
export async function unregisterTaskAsync(taskName) {
|
|
64
|
+
warnDeprecated();
|
|
65
|
+
if (!expoBackgroundFetch.unregisterTaskAsync) {
|
|
66
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'unregisterTaskAsync');
|
|
67
|
+
}
|
|
68
|
+
assertValidTaskName(taskName);
|
|
69
|
+
await expoBackgroundFetch.unregisterTaskAsync(taskName);
|
|
70
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type INativeBackgroundFetchModule = {
|
|
2
|
+
getStatusAsync?(): Promise<number>;
|
|
3
|
+
/** iOS only — Android has no equivalent native call. */
|
|
4
|
+
setMinimumIntervalAsync?(minimumInterval: number): Promise<void>;
|
|
5
|
+
registerTaskAsync?(taskName: string, options: Record<string, unknown>): Promise<void>;
|
|
6
|
+
unregisterTaskAsync?(taskName: string): Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
export declare const expoBackgroundFetch: INativeBackgroundFetchModule;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What iOS does with the return value of a background-fetch task executor — lets the platform
|
|
3
|
+
* schedule future fetches more intelligently. Android ignores it (it has no equivalent concept).
|
|
4
|
+
*/
|
|
5
|
+
export declare enum BackgroundFetchResult {
|
|
6
|
+
/** There was no new data to download. */
|
|
7
|
+
NoData = 1,
|
|
8
|
+
/** New data was successfully downloaded. */
|
|
9
|
+
NewData = 2,
|
|
10
|
+
/** An attempt to download data was made but that attempt failed. */
|
|
11
|
+
Failed = 3
|
|
12
|
+
}
|
|
13
|
+
/** Whether the app can currently receive background-fetch callbacks. */
|
|
14
|
+
export declare enum BackgroundFetchStatus {
|
|
15
|
+
/** The user explicitly disabled background behavior for this app or for the whole system. */
|
|
16
|
+
Denied = 1,
|
|
17
|
+
/**
|
|
18
|
+
* Background updates are unavailable and the user cannot enable them again — e.g. parental
|
|
19
|
+
* controls are in effect for the current user.
|
|
20
|
+
*/
|
|
21
|
+
Restricted = 2,
|
|
22
|
+
/** Background updates are available for the app. */
|
|
23
|
+
Available = 3
|
|
24
|
+
}
|
|
25
|
+
/** Options accepted by {@link registerTaskAsync}. */
|
|
26
|
+
export type IBackgroundFetchOptions = {
|
|
27
|
+
/**
|
|
28
|
+
* Inexact interval in seconds between subsequent repeats of the background fetch alarm. The
|
|
29
|
+
* final interval may differ from the specified one to minimize wakeups and battery usage.
|
|
30
|
+
* - Android defaults to 10 minutes.
|
|
31
|
+
* - iOS calls `setMinimumIntervalAsync` behind the scenes; the platform default is the
|
|
32
|
+
* smallest fetch interval it supports (10-15 minutes).
|
|
33
|
+
*/
|
|
34
|
+
minimumInterval?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Whether to stop receiving background fetch events after the user terminates the app.
|
|
37
|
+
* @default true
|
|
38
|
+
* @platform android
|
|
39
|
+
*/
|
|
40
|
+
stopOnTerminate?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Whether to restart background fetch events when the device has finished booting.
|
|
43
|
+
* @default false
|
|
44
|
+
* @platform android
|
|
45
|
+
*/
|
|
46
|
+
startOnBoot?: boolean;
|
|
47
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What iOS does with the return value of a background-fetch task executor — lets the platform
|
|
3
|
+
* schedule future fetches more intelligently. Android ignores it (it has no equivalent concept).
|
|
4
|
+
*/
|
|
5
|
+
export var BackgroundFetchResult;
|
|
6
|
+
(function (BackgroundFetchResult) {
|
|
7
|
+
/** There was no new data to download. */
|
|
8
|
+
BackgroundFetchResult[BackgroundFetchResult["NoData"] = 1] = "NoData";
|
|
9
|
+
/** New data was successfully downloaded. */
|
|
10
|
+
BackgroundFetchResult[BackgroundFetchResult["NewData"] = 2] = "NewData";
|
|
11
|
+
/** An attempt to download data was made but that attempt failed. */
|
|
12
|
+
BackgroundFetchResult[BackgroundFetchResult["Failed"] = 3] = "Failed";
|
|
13
|
+
})(BackgroundFetchResult || (BackgroundFetchResult = {}));
|
|
14
|
+
/** Whether the app can currently receive background-fetch callbacks. */
|
|
15
|
+
export var BackgroundFetchStatus;
|
|
16
|
+
(function (BackgroundFetchStatus) {
|
|
17
|
+
/** The user explicitly disabled background behavior for this app or for the whole system. */
|
|
18
|
+
BackgroundFetchStatus[BackgroundFetchStatus["Denied"] = 1] = "Denied";
|
|
19
|
+
/**
|
|
20
|
+
* Background updates are unavailable and the user cannot enable them again — e.g. parental
|
|
21
|
+
* controls are in effect for the current user.
|
|
22
|
+
*/
|
|
23
|
+
BackgroundFetchStatus[BackgroundFetchStatus["Restricted"] = 2] = "Restricted";
|
|
24
|
+
/** Background updates are available for the app. */
|
|
25
|
+
BackgroundFetchStatus[BackgroundFetchStatus["Available"] = 3] = "Available";
|
|
26
|
+
})(BackgroundFetchStatus || (BackgroundFetchStatus = {}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../core';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// @symbiote-native/background-fetch/angular: the Angular entry over the framework-agnostic core.
|
|
2
|
+
// Same reasoning as the React/Vue entries — no per-instance state or event stream to wrap in a
|
|
3
|
+
// service, so this is a plain re-export.
|
|
4
|
+
export * from '../core';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/angular/index.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,+FAA+F;AAC/F,yCAAyC;AACzC,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BackgroundFetchStatus } from './types';
|
|
2
|
+
import type { IBackgroundFetchOptions } from './types';
|
|
3
|
+
/** Gets the current background-fetch status, or `null` if the native module is unreachable. */
|
|
4
|
+
export declare function getStatusAsync(): Promise<BackgroundFetchStatus | null>;
|
|
5
|
+
/**
|
|
6
|
+
* Sets the minimum number of seconds that must elapse before another background fetch can be
|
|
7
|
+
* initiated. Advisory only — iOS treats it as a floor, not an exact schedule. No effect on
|
|
8
|
+
* Android.
|
|
9
|
+
*/
|
|
10
|
+
export declare function setMinimumIntervalAsync(minimumInterval: number): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Registers a background-fetch task with the given name. The task must already be defined via
|
|
13
|
+
* `@symbiote-native/task-manager`'s `defineTask` — registration is driven by this package, but
|
|
14
|
+
* execution dispatch is task-manager's job.
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerTaskAsync(taskName: string, options?: IBackgroundFetchOptions): Promise<void>;
|
|
17
|
+
/** Unregisters a background-fetch task, so the app stops receiving fetch callbacks for it. */
|
|
18
|
+
export declare function unregisterTaskAsync(taskName: string): Promise<void>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Platform, UnavailabilityError } from 'expo-modules-core';
|
|
2
|
+
import { isTaskDefined } from '@symbiote-native/task-manager';
|
|
3
|
+
import { expoBackgroundFetch } from './native-module';
|
|
4
|
+
import { BackgroundFetchStatus } from './types';
|
|
5
|
+
const NATIVE_MODULE_NAME = 'BackgroundFetch';
|
|
6
|
+
// expo-background-fetch is upstream-deprecated in favor of expo-background-task (still shipped
|
|
7
|
+
// in sdk-57 alongside it, same as this project's file-system legacy+modern split) — carry the
|
|
8
|
+
// same one-time console warning upstream shows, so an app that adopted the old API notices.
|
|
9
|
+
let didShowDeprecationWarning = false;
|
|
10
|
+
function warnDeprecated() {
|
|
11
|
+
if (didShowDeprecationWarning)
|
|
12
|
+
return;
|
|
13
|
+
didShowDeprecationWarning = true;
|
|
14
|
+
console.warn('@symbiote-native/background-fetch: this API is deprecated. Use @symbiote-native/background-task instead.');
|
|
15
|
+
}
|
|
16
|
+
function assertValidTaskName(taskName) {
|
|
17
|
+
if (!taskName || typeof taskName !== 'string') {
|
|
18
|
+
throw new TypeError('`taskName` must be a non-empty string.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Upstream also warns when running inside Expo Go (`isRunningInExpoGo`, imported from the `expo`
|
|
22
|
+
// meta-package). This project never installs `expo` — every app here is a bare/dev-client build,
|
|
23
|
+
// never Expo Go — so that check has no equivalent and is intentionally not ported.
|
|
24
|
+
/** Gets the current background-fetch status, or `null` if the native module is unreachable. */
|
|
25
|
+
export async function getStatusAsync() {
|
|
26
|
+
warnDeprecated();
|
|
27
|
+
if (Platform.OS === 'android') {
|
|
28
|
+
return BackgroundFetchStatus.Available;
|
|
29
|
+
}
|
|
30
|
+
if (!expoBackgroundFetch.getStatusAsync) {
|
|
31
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getStatusAsync');
|
|
32
|
+
}
|
|
33
|
+
return expoBackgroundFetch.getStatusAsync();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Sets the minimum number of seconds that must elapse before another background fetch can be
|
|
37
|
+
* initiated. Advisory only — iOS treats it as a floor, not an exact schedule. No effect on
|
|
38
|
+
* Android.
|
|
39
|
+
*/
|
|
40
|
+
export async function setMinimumIntervalAsync(minimumInterval) {
|
|
41
|
+
warnDeprecated();
|
|
42
|
+
if (!expoBackgroundFetch.setMinimumIntervalAsync)
|
|
43
|
+
return;
|
|
44
|
+
await expoBackgroundFetch.setMinimumIntervalAsync(minimumInterval);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Registers a background-fetch task with the given name. The task must already be defined via
|
|
48
|
+
* `@symbiote-native/task-manager`'s `defineTask` — registration is driven by this package, but
|
|
49
|
+
* execution dispatch is task-manager's job.
|
|
50
|
+
*/
|
|
51
|
+
export async function registerTaskAsync(taskName, options = {}) {
|
|
52
|
+
warnDeprecated();
|
|
53
|
+
if (!expoBackgroundFetch.registerTaskAsync) {
|
|
54
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'registerTaskAsync');
|
|
55
|
+
}
|
|
56
|
+
if (!isTaskDefined(taskName)) {
|
|
57
|
+
throw new Error(`Task '${taskName}' is not defined. You must define a task using defineTask (from @symbiote-native/task-manager) before registering.`);
|
|
58
|
+
}
|
|
59
|
+
assertValidTaskName(taskName);
|
|
60
|
+
await expoBackgroundFetch.registerTaskAsync(taskName, options);
|
|
61
|
+
}
|
|
62
|
+
/** Unregisters a background-fetch task, so the app stops receiving fetch callbacks for it. */
|
|
63
|
+
export async function unregisterTaskAsync(taskName) {
|
|
64
|
+
warnDeprecated();
|
|
65
|
+
if (!expoBackgroundFetch.unregisterTaskAsync) {
|
|
66
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'unregisterTaskAsync');
|
|
67
|
+
}
|
|
68
|
+
assertValidTaskName(taskName);
|
|
69
|
+
await expoBackgroundFetch.unregisterTaskAsync(taskName);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=background-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"background-fetch.js","sourceRoot":"","sources":["../../src/core/background-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGhD,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAE7C,+FAA+F;AAC/F,8FAA8F;AAC9F,4FAA4F;AAC5F,IAAI,yBAAyB,GAAG,KAAK,CAAC;AACtC,SAAS,cAAc;IACrB,IAAI,yBAAyB;QAAE,OAAO;IACtC,yBAAyB,GAAG,IAAI,CAAC;IACjC,OAAO,CAAC,IAAI,CACV,0GAA0G,CAC3G,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAiB;IAC5C,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC9C,MAAM,IAAI,SAAS,CAAC,wCAAwC,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED,iGAAiG;AACjG,iGAAiG;AACjG,mFAAmF;AAEnF,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,cAAc,EAAE,CAAC;IACjB,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,qBAAqB,CAAC,SAAS,CAAC;IACzC,CAAC;IACD,IAAI,CAAC,mBAAmB,CAAC,cAAc,EAAE,CAAC;QACxC,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,mBAAmB,CAAC,cAAc,EAAoC,CAAC;AAChF,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,eAAuB;IAEvB,cAAc,EAAE,CAAC;IACjB,IAAI,CAAC,mBAAmB,CAAC,uBAAuB;QAAE,OAAO;IACzD,MAAM,mBAAmB,CAAC,uBAAuB,CAAC,eAAe,CAAC,CAAC;AACrE,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAgB,EAChB,UAAmC,EAAE;IAErC,cAAc,EAAE,CAAC;IACjB,IAAI,CAAC,mBAAmB,CAAC,iBAAiB,EAAE,CAAC;QAC3C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,SAAS,QAAQ,oHAAoH,CACtI,CAAC;IACJ,CAAC;IACD,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,mBAAmB,CAAC,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACjE,CAAC;AAED,8FAA8F;AAC9F,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAAgB;IACxD,cAAc,EAAE,CAAC;IACjB,IAAI,CAAC,mBAAmB,CAAC,mBAAmB,EAAE,CAAC;QAC7C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,CAAC;IAC3E,CAAC;IACD,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,mBAAmB,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type INativeBackgroundFetchModule = {
|
|
2
|
+
getStatusAsync?(): Promise<number>;
|
|
3
|
+
/** iOS only — Android has no equivalent native call. */
|
|
4
|
+
setMinimumIntervalAsync?(minimumInterval: number): Promise<void>;
|
|
5
|
+
registerTaskAsync?(taskName: string, options: Record<string, unknown>): Promise<void>;
|
|
6
|
+
unregisterTaskAsync?(taskName: string): Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
export declare const expoBackgroundFetch: INativeBackgroundFetchModule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,iCAAiC,GAAG,qBAAqB,CAAC;AAgBhE,MAAM,CAAC,MAAM,mBAAmB,GAC9B,mBAAmB,CACjB,iCAAiC,CAClC,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What iOS does with the return value of a background-fetch task executor — lets the platform
|
|
3
|
+
* schedule future fetches more intelligently. Android ignores it (it has no equivalent concept).
|
|
4
|
+
*/
|
|
5
|
+
export declare enum BackgroundFetchResult {
|
|
6
|
+
/** There was no new data to download. */
|
|
7
|
+
NoData = 1,
|
|
8
|
+
/** New data was successfully downloaded. */
|
|
9
|
+
NewData = 2,
|
|
10
|
+
/** An attempt to download data was made but that attempt failed. */
|
|
11
|
+
Failed = 3
|
|
12
|
+
}
|
|
13
|
+
/** Whether the app can currently receive background-fetch callbacks. */
|
|
14
|
+
export declare enum BackgroundFetchStatus {
|
|
15
|
+
/** The user explicitly disabled background behavior for this app or for the whole system. */
|
|
16
|
+
Denied = 1,
|
|
17
|
+
/**
|
|
18
|
+
* Background updates are unavailable and the user cannot enable them again — e.g. parental
|
|
19
|
+
* controls are in effect for the current user.
|
|
20
|
+
*/
|
|
21
|
+
Restricted = 2,
|
|
22
|
+
/** Background updates are available for the app. */
|
|
23
|
+
Available = 3
|
|
24
|
+
}
|
|
25
|
+
/** Options accepted by {@link registerTaskAsync}. */
|
|
26
|
+
export type IBackgroundFetchOptions = {
|
|
27
|
+
/**
|
|
28
|
+
* Inexact interval in seconds between subsequent repeats of the background fetch alarm. The
|
|
29
|
+
* final interval may differ from the specified one to minimize wakeups and battery usage.
|
|
30
|
+
* - Android defaults to 10 minutes.
|
|
31
|
+
* - iOS calls `setMinimumIntervalAsync` behind the scenes; the platform default is the
|
|
32
|
+
* smallest fetch interval it supports (10-15 minutes).
|
|
33
|
+
*/
|
|
34
|
+
minimumInterval?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Whether to stop receiving background fetch events after the user terminates the app.
|
|
37
|
+
* @default true
|
|
38
|
+
* @platform android
|
|
39
|
+
*/
|
|
40
|
+
stopOnTerminate?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Whether to restart background fetch events when the device has finished booting.
|
|
43
|
+
* @default false
|
|
44
|
+
* @platform android
|
|
45
|
+
*/
|
|
46
|
+
startOnBoot?: boolean;
|
|
47
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What iOS does with the return value of a background-fetch task executor — lets the platform
|
|
3
|
+
* schedule future fetches more intelligently. Android ignores it (it has no equivalent concept).
|
|
4
|
+
*/
|
|
5
|
+
export var BackgroundFetchResult;
|
|
6
|
+
(function (BackgroundFetchResult) {
|
|
7
|
+
/** There was no new data to download. */
|
|
8
|
+
BackgroundFetchResult[BackgroundFetchResult["NoData"] = 1] = "NoData";
|
|
9
|
+
/** New data was successfully downloaded. */
|
|
10
|
+
BackgroundFetchResult[BackgroundFetchResult["NewData"] = 2] = "NewData";
|
|
11
|
+
/** An attempt to download data was made but that attempt failed. */
|
|
12
|
+
BackgroundFetchResult[BackgroundFetchResult["Failed"] = 3] = "Failed";
|
|
13
|
+
})(BackgroundFetchResult || (BackgroundFetchResult = {}));
|
|
14
|
+
/** Whether the app can currently receive background-fetch callbacks. */
|
|
15
|
+
export var BackgroundFetchStatus;
|
|
16
|
+
(function (BackgroundFetchStatus) {
|
|
17
|
+
/** The user explicitly disabled background behavior for this app or for the whole system. */
|
|
18
|
+
BackgroundFetchStatus[BackgroundFetchStatus["Denied"] = 1] = "Denied";
|
|
19
|
+
/**
|
|
20
|
+
* Background updates are unavailable and the user cannot enable them again — e.g. parental
|
|
21
|
+
* controls are in effect for the current user.
|
|
22
|
+
*/
|
|
23
|
+
BackgroundFetchStatus[BackgroundFetchStatus["Restricted"] = 2] = "Restricted";
|
|
24
|
+
/** Background updates are available for the app. */
|
|
25
|
+
BackgroundFetchStatus[BackgroundFetchStatus["Available"] = 3] = "Available";
|
|
26
|
+
})(BackgroundFetchStatus || (BackgroundFetchStatus = {}));
|
|
27
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAN,IAAY,qBAOX;AAPD,WAAY,qBAAqB;IAC/B,yCAAyC;IACzC,qEAAU,CAAA;IACV,4CAA4C;IAC5C,uEAAW,CAAA;IACX,oEAAoE;IACpE,qEAAU,CAAA;AACZ,CAAC,EAPW,qBAAqB,KAArB,qBAAqB,QAOhC;AAED,wEAAwE;AACxE,MAAM,CAAN,IAAY,qBAUX;AAVD,WAAY,qBAAqB;IAC/B,6FAA6F;IAC7F,qEAAU,CAAA;IACV;;;OAGG;IACH,6EAAc,CAAA;IACd,oDAAoD;IACpD,2EAAa,CAAA;AACf,CAAC,EAVW,qBAAqB,KAArB,qBAAqB,QAUhC"}
|
package/native-link.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"android": {
|
|
3
|
+
"gradleProjectName": "expo-background-fetch",
|
|
4
|
+
"modules": [
|
|
5
|
+
{
|
|
6
|
+
"importPath": "expo.modules.backgroundfetch.BackgroundFetchModule",
|
|
7
|
+
"className": "BackgroundFetchModule",
|
|
8
|
+
"nativeName": "ExpoBackgroundFetch"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"ios": {
|
|
13
|
+
"infoPlistArrayKeys": { "UIBackgroundModes": ["fetch"] }
|
|
14
|
+
}
|
|
15
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@symbiote-native/background-fetch",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "expo-background-fetch wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Periodic background fetch, registered through @symbiote-native/task-manager. Deprecated upstream in favor of expo-background-task; ported for parity with what Expo still ships.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/OneEyed1366/symbiote-native.git",
|
|
9
|
+
"directory": "packages/background-fetch"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/OneEyed1366/symbiote-native/tree/master/packages/background-fetch#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/OneEyed1366/symbiote-native/issues"
|
|
14
|
+
},
|
|
15
|
+
"author": "Andrey Prokopenko <psevdoproger@gmail.com>",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"react-native",
|
|
18
|
+
"symbiote-native",
|
|
19
|
+
"expo-background-fetch",
|
|
20
|
+
"react",
|
|
21
|
+
"vue",
|
|
22
|
+
"svelte",
|
|
23
|
+
"solid",
|
|
24
|
+
"angular",
|
|
25
|
+
"background",
|
|
26
|
+
"fetch"
|
|
27
|
+
],
|
|
28
|
+
"type": "module",
|
|
29
|
+
"main": "./build/core/index.js",
|
|
30
|
+
"module": "./build/core/index.js",
|
|
31
|
+
"types": "./build/core/index.d.ts",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./build/core/index.d.ts",
|
|
35
|
+
"default": "./build/core/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./vue": {
|
|
38
|
+
"types": "./build/core/index.d.ts",
|
|
39
|
+
"default": "./build/core/index.js"
|
|
40
|
+
},
|
|
41
|
+
"./react": {
|
|
42
|
+
"types": "./build/core/index.d.ts",
|
|
43
|
+
"default": "./build/core/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./svelte": {
|
|
46
|
+
"types": "./build/core/index.d.ts",
|
|
47
|
+
"default": "./build/core/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./solid": {
|
|
50
|
+
"types": "./build/core/index.d.ts",
|
|
51
|
+
"default": "./build/core/index.js"
|
|
52
|
+
},
|
|
53
|
+
"./angular": {
|
|
54
|
+
"types": "./build-ngc/angular/index.d.ts",
|
|
55
|
+
"react-native": "./build-ngc/angular/index.js",
|
|
56
|
+
"default": "./src/angular/index.ts"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"files": [
|
|
60
|
+
"src",
|
|
61
|
+
"build",
|
|
62
|
+
"build-ngc",
|
|
63
|
+
"native-link.json",
|
|
64
|
+
"!src/**/*.test.*",
|
|
65
|
+
"!src/**/*.spec.*",
|
|
66
|
+
"!src/**/*.detox.*"
|
|
67
|
+
],
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public"
|
|
70
|
+
},
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"expo-background-fetch": "57.0.15",
|
|
73
|
+
"expo-modules-core": "57.0.5",
|
|
74
|
+
"@symbiote-native/task-manager": "0.1.0"
|
|
75
|
+
},
|
|
76
|
+
"peerDependencies": {
|
|
77
|
+
"@angular/core": ">=20",
|
|
78
|
+
"@vue/runtime-core": "^3.5.13",
|
|
79
|
+
"react": ">=19.0.0",
|
|
80
|
+
"react-native": ">=0.86",
|
|
81
|
+
"solid-js": ">=1.9.0",
|
|
82
|
+
"svelte": ">=5.56.0",
|
|
83
|
+
"vue": ">=3.5.0",
|
|
84
|
+
"@symbiote-native/engine": "^1.3.0",
|
|
85
|
+
"@symbiote-native/angular": "^3.1.0",
|
|
86
|
+
"@symbiote-native/react": "^3.0.2",
|
|
87
|
+
"@symbiote-native/svelte": "^3.0.2",
|
|
88
|
+
"@symbiote-native/vue": "^3.0.2",
|
|
89
|
+
"@symbiote-native/solid": "^3.0.2"
|
|
90
|
+
},
|
|
91
|
+
"peerDependenciesMeta": {
|
|
92
|
+
"@symbiote-native/angular": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"@symbiote-native/react": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"@symbiote-native/solid": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"@symbiote-native/svelte": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"@symbiote-native/vue": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"@angular/core": {
|
|
108
|
+
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"@vue/runtime-core": {
|
|
111
|
+
"optional": true
|
|
112
|
+
},
|
|
113
|
+
"react": {
|
|
114
|
+
"optional": true
|
|
115
|
+
},
|
|
116
|
+
"solid-js": {
|
|
117
|
+
"optional": true
|
|
118
|
+
},
|
|
119
|
+
"svelte": {
|
|
120
|
+
"optional": true
|
|
121
|
+
},
|
|
122
|
+
"vue": {
|
|
123
|
+
"optional": true
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"devDependencies": {
|
|
127
|
+
"@angular/compiler": "~22.0.8",
|
|
128
|
+
"@angular/compiler-cli": "~22.0.8",
|
|
129
|
+
"@angular/core": "~22.0.8",
|
|
130
|
+
"@types/node": "^26.0.0",
|
|
131
|
+
"@types/react": "^19.2.0",
|
|
132
|
+
"@vue/runtime-core": "^3.5.13",
|
|
133
|
+
"react": "19.2.3",
|
|
134
|
+
"solid-js": "^1.9.14",
|
|
135
|
+
"svelte": "^5.56.0",
|
|
136
|
+
"typescript": "~6.0.0",
|
|
137
|
+
"@symbiote-native/engine": "1.3.0",
|
|
138
|
+
"@symbiote-native/svelte": "3.0.2",
|
|
139
|
+
"@symbiote-native/angular": "3.1.0",
|
|
140
|
+
"@symbiote-native/vue": "3.0.2",
|
|
141
|
+
"@symbiote-native/react": "3.0.2",
|
|
142
|
+
"@symbiote-native/solid": "3.0.2",
|
|
143
|
+
"@symbiote-native/test-utils": "0.4.2"
|
|
144
|
+
},
|
|
145
|
+
"scripts": {
|
|
146
|
+
"typecheck": "tsc --build",
|
|
147
|
+
"clean": "rm -rf build-ngc",
|
|
148
|
+
"ng:build": "pnpm run clean && ngc -p tsconfig.angular.json",
|
|
149
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Platform, UnavailabilityError } from 'expo-modules-core';
|
|
2
|
+
import { isTaskDefined } from '@symbiote-native/task-manager';
|
|
3
|
+
import { expoBackgroundFetch } from './native-module';
|
|
4
|
+
import { BackgroundFetchStatus } from './types';
|
|
5
|
+
import type { IBackgroundFetchOptions } from './types';
|
|
6
|
+
|
|
7
|
+
const NATIVE_MODULE_NAME = 'BackgroundFetch';
|
|
8
|
+
|
|
9
|
+
// expo-background-fetch is upstream-deprecated in favor of expo-background-task (still shipped
|
|
10
|
+
// in sdk-57 alongside it, same as this project's file-system legacy+modern split) — carry the
|
|
11
|
+
// same one-time console warning upstream shows, so an app that adopted the old API notices.
|
|
12
|
+
let didShowDeprecationWarning = false;
|
|
13
|
+
function warnDeprecated(): void {
|
|
14
|
+
if (didShowDeprecationWarning) return;
|
|
15
|
+
didShowDeprecationWarning = true;
|
|
16
|
+
console.warn(
|
|
17
|
+
'@symbiote-native/background-fetch: this API is deprecated. Use @symbiote-native/background-task instead.',
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function assertValidTaskName(taskName: unknown): asserts taskName is string {
|
|
22
|
+
if (!taskName || typeof taskName !== 'string') {
|
|
23
|
+
throw new TypeError('`taskName` must be a non-empty string.');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Upstream also warns when running inside Expo Go (`isRunningInExpoGo`, imported from the `expo`
|
|
28
|
+
// meta-package). This project never installs `expo` — every app here is a bare/dev-client build,
|
|
29
|
+
// never Expo Go — so that check has no equivalent and is intentionally not ported.
|
|
30
|
+
|
|
31
|
+
/** Gets the current background-fetch status, or `null` if the native module is unreachable. */
|
|
32
|
+
export async function getStatusAsync(): Promise<BackgroundFetchStatus | null> {
|
|
33
|
+
warnDeprecated();
|
|
34
|
+
if (Platform.OS === 'android') {
|
|
35
|
+
return BackgroundFetchStatus.Available;
|
|
36
|
+
}
|
|
37
|
+
if (!expoBackgroundFetch.getStatusAsync) {
|
|
38
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getStatusAsync');
|
|
39
|
+
}
|
|
40
|
+
return expoBackgroundFetch.getStatusAsync() as Promise<BackgroundFetchStatus>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Sets the minimum number of seconds that must elapse before another background fetch can be
|
|
45
|
+
* initiated. Advisory only — iOS treats it as a floor, not an exact schedule. No effect on
|
|
46
|
+
* Android.
|
|
47
|
+
*/
|
|
48
|
+
export async function setMinimumIntervalAsync(
|
|
49
|
+
minimumInterval: number,
|
|
50
|
+
): Promise<void> {
|
|
51
|
+
warnDeprecated();
|
|
52
|
+
if (!expoBackgroundFetch.setMinimumIntervalAsync) return;
|
|
53
|
+
await expoBackgroundFetch.setMinimumIntervalAsync(minimumInterval);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Registers a background-fetch task with the given name. The task must already be defined via
|
|
58
|
+
* `@symbiote-native/task-manager`'s `defineTask` — registration is driven by this package, but
|
|
59
|
+
* execution dispatch is task-manager's job.
|
|
60
|
+
*/
|
|
61
|
+
export async function registerTaskAsync(
|
|
62
|
+
taskName: string,
|
|
63
|
+
options: IBackgroundFetchOptions = {},
|
|
64
|
+
): Promise<void> {
|
|
65
|
+
warnDeprecated();
|
|
66
|
+
if (!expoBackgroundFetch.registerTaskAsync) {
|
|
67
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'registerTaskAsync');
|
|
68
|
+
}
|
|
69
|
+
if (!isTaskDefined(taskName)) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Task '${taskName}' is not defined. You must define a task using defineTask (from @symbiote-native/task-manager) before registering.`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
assertValidTaskName(taskName);
|
|
75
|
+
await expoBackgroundFetch.registerTaskAsync(taskName, options);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Unregisters a background-fetch task, so the app stops receiving fetch callbacks for it. */
|
|
79
|
+
export async function unregisterTaskAsync(taskName: string): Promise<void> {
|
|
80
|
+
warnDeprecated();
|
|
81
|
+
if (!expoBackgroundFetch.unregisterTaskAsync) {
|
|
82
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'unregisterTaskAsync');
|
|
83
|
+
}
|
|
84
|
+
assertValidTaskName(taskName);
|
|
85
|
+
await expoBackgroundFetch.unregisterTaskAsync(taskName);
|
|
86
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
2
|
+
|
|
3
|
+
const EXPO_BACKGROUND_FETCH_MODULE_NAME = 'ExpoBackgroundFetch';
|
|
4
|
+
|
|
5
|
+
// Every method is optional — each call site checks for its presence before calling through and
|
|
6
|
+
// throws an UnavailabilityError itself, matching upstream's own per-platform capability checks
|
|
7
|
+
// (same pattern as packages/task-manager/src/core/native-module.ts's INativeTaskManagerModule).
|
|
8
|
+
export type INativeBackgroundFetchModule = {
|
|
9
|
+
getStatusAsync?(): Promise<number>;
|
|
10
|
+
/** iOS only — Android has no equivalent native call. */
|
|
11
|
+
setMinimumIntervalAsync?(minimumInterval: number): Promise<void>;
|
|
12
|
+
registerTaskAsync?(
|
|
13
|
+
taskName: string,
|
|
14
|
+
options: Record<string, unknown>,
|
|
15
|
+
): Promise<void>;
|
|
16
|
+
unregisterTaskAsync?(taskName: string): Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const expoBackgroundFetch =
|
|
20
|
+
requireNativeModule<INativeBackgroundFetchModule>(
|
|
21
|
+
EXPO_BACKGROUND_FETCH_MODULE_NAME,
|
|
22
|
+
);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What iOS does with the return value of a background-fetch task executor — lets the platform
|
|
3
|
+
* schedule future fetches more intelligently. Android ignores it (it has no equivalent concept).
|
|
4
|
+
*/
|
|
5
|
+
export enum BackgroundFetchResult {
|
|
6
|
+
/** There was no new data to download. */
|
|
7
|
+
NoData = 1,
|
|
8
|
+
/** New data was successfully downloaded. */
|
|
9
|
+
NewData = 2,
|
|
10
|
+
/** An attempt to download data was made but that attempt failed. */
|
|
11
|
+
Failed = 3,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Whether the app can currently receive background-fetch callbacks. */
|
|
15
|
+
export enum BackgroundFetchStatus {
|
|
16
|
+
/** The user explicitly disabled background behavior for this app or for the whole system. */
|
|
17
|
+
Denied = 1,
|
|
18
|
+
/**
|
|
19
|
+
* Background updates are unavailable and the user cannot enable them again — e.g. parental
|
|
20
|
+
* controls are in effect for the current user.
|
|
21
|
+
*/
|
|
22
|
+
Restricted = 2,
|
|
23
|
+
/** Background updates are available for the app. */
|
|
24
|
+
Available = 3,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Options accepted by {@link registerTaskAsync}. */
|
|
28
|
+
export type IBackgroundFetchOptions = {
|
|
29
|
+
/**
|
|
30
|
+
* Inexact interval in seconds between subsequent repeats of the background fetch alarm. The
|
|
31
|
+
* final interval may differ from the specified one to minimize wakeups and battery usage.
|
|
32
|
+
* - Android defaults to 10 minutes.
|
|
33
|
+
* - iOS calls `setMinimumIntervalAsync` behind the scenes; the platform default is the
|
|
34
|
+
* smallest fetch interval it supports (10-15 minutes).
|
|
35
|
+
*/
|
|
36
|
+
minimumInterval?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Whether to stop receiving background fetch events after the user terminates the app.
|
|
39
|
+
* @default true
|
|
40
|
+
* @platform android
|
|
41
|
+
*/
|
|
42
|
+
stopOnTerminate?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Whether to restart background fetch events when the device has finished booting.
|
|
45
|
+
* @default false
|
|
46
|
+
* @platform android
|
|
47
|
+
*/
|
|
48
|
+
startOnBoot?: boolean;
|
|
49
|
+
};
|