@zykeco/expo-background-task 1.0.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.
Files changed (49) hide show
  1. package/.eslintrc.js +2 -0
  2. package/CHANGELOG.md +185 -0
  3. package/README.md +115 -0
  4. package/android/build.gradle +21 -0
  5. package/android/proguard-rules.pro +1 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/java/expo/modules/backgroundtask/BackgroundTaskConsumer.kt +60 -0
  8. package/android/src/main/java/expo/modules/backgroundtask/BackgroundTaskExceptions.kt +13 -0
  9. package/android/src/main/java/expo/modules/backgroundtask/BackgroundTaskModule.kt +56 -0
  10. package/android/src/main/java/expo/modules/backgroundtask/BackgroundTaskScheduler.kt +294 -0
  11. package/android/src/main/java/expo/modules/backgroundtask/BackgroundTaskWork.kt +38 -0
  12. package/app.plugin.js +1 -0
  13. package/build/BackgroundTask.d.ts +63 -0
  14. package/build/BackgroundTask.d.ts.map +1 -0
  15. package/build/BackgroundTask.js +141 -0
  16. package/build/BackgroundTask.js.map +1 -0
  17. package/build/BackgroundTask.types.d.ts +71 -0
  18. package/build/BackgroundTask.types.d.ts.map +1 -0
  19. package/build/BackgroundTask.types.js +31 -0
  20. package/build/BackgroundTask.types.js.map +1 -0
  21. package/build/ExpoBackgroundTaskModule.d.ts +14 -0
  22. package/build/ExpoBackgroundTaskModule.d.ts.map +1 -0
  23. package/build/ExpoBackgroundTaskModule.js +3 -0
  24. package/build/ExpoBackgroundTaskModule.js.map +1 -0
  25. package/build/ExpoBackgroundTaskModule.web.d.ts +6 -0
  26. package/build/ExpoBackgroundTaskModule.web.d.ts.map +1 -0
  27. package/build/ExpoBackgroundTaskModule.web.js +7 -0
  28. package/build/ExpoBackgroundTaskModule.web.js.map +1 -0
  29. package/expo-module.config.json +10 -0
  30. package/ios/BackgorundTaskExceptions.swift +48 -0
  31. package/ios/BackgroundTaskAppDelegate.swift +74 -0
  32. package/ios/BackgroundTaskConstants.swift +13 -0
  33. package/ios/BackgroundTaskConsumer.swift +62 -0
  34. package/ios/BackgroundTaskDebugHelper.swift +21 -0
  35. package/ios/BackgroundTaskModule.swift +80 -0
  36. package/ios/BackgroundTaskRecords.swift +12 -0
  37. package/ios/BackgroundTaskScheduler.swift +206 -0
  38. package/ios/ExpoBackgroundTask.podspec +33 -0
  39. package/package.json +42 -0
  40. package/plugin/build/withBackgroundTask.d.ts +3 -0
  41. package/plugin/build/withBackgroundTask.js +32 -0
  42. package/plugin/src/withBackgroundTask.ts +39 -0
  43. package/plugin/tsconfig.json +9 -0
  44. package/plugin/tsconfig.tsbuildinfo +1 -0
  45. package/src/BackgroundTask.ts +162 -0
  46. package/src/BackgroundTask.types.ts +75 -0
  47. package/src/ExpoBackgroundTaskModule.ts +16 -0
  48. package/src/ExpoBackgroundTaskModule.web.ts +7 -0
  49. package/tsconfig.json +9 -0
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const config_plugins_1 = require("expo/config-plugins");
4
+ const pkg = require('expo-background-task/package.json');
5
+ const withBackgroundTask = (config) => {
6
+ return (0, config_plugins_1.withInfoPlist)(config, (config) => {
7
+ // Enable background mode processing
8
+ if (!Array.isArray(config.modResults.UIBackgroundModes)) {
9
+ config.modResults.UIBackgroundModes = [];
10
+ }
11
+ if (!config.modResults.UIBackgroundModes.includes('processing')) {
12
+ config.modResults.UIBackgroundModes.push('processing');
13
+ }
14
+ if (!config.modResults.UIBackgroundModes.includes('fetch')) {
15
+ config.modResults.UIBackgroundModes.push('fetch');
16
+ }
17
+ // With the new background task module we need to install the identifier in the Info.plist:
18
+ // BGTaskSchedulerPermittedIdentifiers should be an array of strings - we need to
19
+ // define our own identifier: com.expo.modules.backgroundtask.taskidentifer
20
+ if (!Array.isArray(config.modResults.BGTaskSchedulerPermittedIdentifiers)) {
21
+ config.modResults.BGTaskSchedulerPermittedIdentifiers = [];
22
+ }
23
+ if (!config.modResults.BGTaskSchedulerPermittedIdentifiers.includes('com.expo.modules.backgroundtask.processing')) {
24
+ config.modResults.BGTaskSchedulerPermittedIdentifiers = [
25
+ ...(config.modResults.BGTaskSchedulerPermittedIdentifiers || []),
26
+ 'com.expo.modules.backgroundtask.processing',
27
+ ];
28
+ }
29
+ return config;
30
+ });
31
+ };
32
+ exports.default = (0, config_plugins_1.createRunOncePlugin)(withBackgroundTask, pkg.name, pkg.version);
@@ -0,0 +1,39 @@
1
+ import { ConfigPlugin, createRunOncePlugin, withInfoPlist } from 'expo/config-plugins';
2
+
3
+ const pkg = require('expo-background-task/package.json');
4
+
5
+ const withBackgroundTask: ConfigPlugin = (config) => {
6
+ return withInfoPlist(config, (config) => {
7
+ // Enable background mode processing
8
+ if (!Array.isArray(config.modResults.UIBackgroundModes)) {
9
+ config.modResults.UIBackgroundModes = [];
10
+ }
11
+ if (!config.modResults.UIBackgroundModes.includes('processing')) {
12
+ config.modResults.UIBackgroundModes.push('processing');
13
+ }
14
+ if (!config.modResults.UIBackgroundModes.includes('fetch')) {
15
+ config.modResults.UIBackgroundModes.push('fetch');
16
+ }
17
+
18
+ // With the new background task module we need to install the identifier in the Info.plist:
19
+ // BGTaskSchedulerPermittedIdentifiers should be an array of strings - we need to
20
+ // define our own identifier: com.expo.modules.backgroundtask.taskidentifer
21
+ if (!Array.isArray(config.modResults.BGTaskSchedulerPermittedIdentifiers)) {
22
+ config.modResults.BGTaskSchedulerPermittedIdentifiers = [];
23
+ }
24
+ if (
25
+ !(config.modResults.BGTaskSchedulerPermittedIdentifiers as string[]).includes(
26
+ 'com.expo.modules.backgroundtask.processing'
27
+ )
28
+ ) {
29
+ config.modResults.BGTaskSchedulerPermittedIdentifiers = [
30
+ ...((config.modResults.BGTaskSchedulerPermittedIdentifiers as string[]) || []),
31
+ 'com.expo.modules.backgroundtask.processing',
32
+ ];
33
+ }
34
+
35
+ return config;
36
+ });
37
+ };
38
+
39
+ export default createRunOncePlugin(withBackgroundTask, pkg.name, pkg.version);
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "expo-module-scripts/tsconfig.plugin",
3
+ "compilerOptions": {
4
+ "outDir": "build",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["./src"],
8
+ "exclude": ["**/__mocks__/*", "**/__tests__/*"]
9
+ }
@@ -0,0 +1 @@
1
+ {"root":["./src/withbackgroundtask.ts"],"version":"5.9.3"}
@@ -0,0 +1,162 @@
1
+ import { isRunningInExpoGo } from 'expo';
2
+ import { Platform, UnavailabilityError } from 'expo-modules-core';
3
+ import * as TaskManager from 'expo-task-manager';
4
+
5
+ import { BackgroundTaskOptions, BackgroundTaskStatus } from './BackgroundTask.types';
6
+ import ExpoBackgroundTaskModule from './ExpoBackgroundTaskModule';
7
+
8
+ // Flag to warn about running on Apple simulator
9
+ let warnAboutRunningOniOSSimulator = false;
10
+
11
+ let warnedAboutExpoGo = false;
12
+
13
+ function _validate(taskName: unknown) {
14
+ if (isRunningInExpoGo()) {
15
+ if (!warnedAboutExpoGo) {
16
+ const message =
17
+ '`Background Task` functionality is not available in Expo Go:\n' +
18
+ 'You can use this API and any others in a development build. Learn more: https://expo.fyi/dev-client.';
19
+ console.warn(message);
20
+ warnedAboutExpoGo = true;
21
+ }
22
+ }
23
+ if (!taskName || typeof taskName !== 'string') {
24
+ throw new TypeError('`taskName` must be a non-empty string.');
25
+ }
26
+ }
27
+
28
+ // @needsAudit
29
+ /**
30
+ * Returns the status for the Background Task API. On web, it always returns `BackgroundTaskStatus.Restricted`,
31
+ * while on native platforms it returns `BackgroundTaskStatus.Available`.
32
+ *
33
+ * @returns A BackgroundTaskStatus enum value or `null` if not available.
34
+ */
35
+ export const getStatusAsync = async (): Promise<BackgroundTaskStatus> => {
36
+ if (!ExpoBackgroundTaskModule.getStatusAsync) {
37
+ throw new UnavailabilityError('BackgroundTask', 'getStatusAsync');
38
+ }
39
+
40
+ return isRunningInExpoGo()
41
+ ? BackgroundTaskStatus.Restricted
42
+ : ExpoBackgroundTaskModule.getStatusAsync();
43
+ };
44
+
45
+ // @needsAudit
46
+ /**
47
+ * Registers a background task with the given name. Registered tasks are saved in persistent storage and restored once the app is initialized.
48
+ * @param taskName Name of the task to register. The task needs to be defined first - see [`TaskManager.defineTask`](task-manager/#taskmanagerdefinetasktaskname-taskexecutor)
49
+ * for more details.
50
+ * @param options An object containing the background task options.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * import * as TaskManager from 'expo-task-manager';
55
+ *
56
+ * // Register the task outside of the component
57
+ * TaskManager.defineTask(BACKGROUND_TASK_IDENTIFIER, () => {
58
+ * try {
59
+ * await AsyncStorage.setItem(LAST_TASK_DATE_KEY, Date.now().toString());
60
+ * } catch (error) {
61
+ * console.error('Failed to save the last fetch date', error);
62
+ * return BackgroundTaskResult.Failed;
63
+ * }
64
+ * return BackgroundTaskResult.Success;
65
+ * });
66
+ * ```
67
+ *
68
+ * You can now use the `registerTaskAsync` function to register the task:
69
+ *
70
+ * ```ts
71
+ * BackgroundTask.registerTaskAsync(BACKGROUND_TASK_IDENTIFIER, {});
72
+ * ```
73
+ */
74
+ export async function registerTaskAsync(
75
+ taskName: string,
76
+ options: BackgroundTaskOptions = {}
77
+ ): Promise<void> {
78
+ if (!ExpoBackgroundTaskModule.registerTaskAsync) {
79
+ throw new UnavailabilityError('BackgroundTask', 'registerTaskAsync');
80
+ }
81
+ if (!TaskManager.isTaskDefined(taskName)) {
82
+ throw new Error(
83
+ `Task '${taskName}' is not defined. You must define a task using TaskManager.defineTask before registering.`
84
+ );
85
+ }
86
+
87
+ if ((await ExpoBackgroundTaskModule.getStatusAsync()) === BackgroundTaskStatus.Restricted) {
88
+ if (!warnAboutRunningOniOSSimulator) {
89
+ const message =
90
+ Platform.OS === 'ios'
91
+ ? `Background tasks are not supported on iOS simulators. Skipped registering task: ${taskName}.`
92
+ : `Background tasks are not available in the current environment. Skipped registering task: ${taskName}.`;
93
+ console.warn(message);
94
+ warnAboutRunningOniOSSimulator = true;
95
+ }
96
+ return;
97
+ }
98
+ _validate(taskName);
99
+ if (await TaskManager.isTaskRegisteredAsync(taskName)) {
100
+ return;
101
+ }
102
+ await ExpoBackgroundTaskModule.registerTaskAsync(taskName, options);
103
+ }
104
+
105
+ // @needsAudit
106
+ /**
107
+ * Unregisters a background task, so the application will no longer be executing this task.
108
+ * @param taskName Name of the task to unregister.
109
+ * @return A promise which fulfils when the task is fully unregistered.
110
+ */
111
+ export async function unregisterTaskAsync(taskName: string): Promise<void> {
112
+ if (!ExpoBackgroundTaskModule.unregisterTaskAsync) {
113
+ throw new UnavailabilityError('BackgroundTask', 'unregisterTaskAsync');
114
+ }
115
+ _validate(taskName);
116
+ if (!(await TaskManager.isTaskRegisteredAsync(taskName))) {
117
+ return;
118
+ }
119
+ await ExpoBackgroundTaskModule.unregisterTaskAsync(taskName);
120
+ }
121
+
122
+ // @needsAudit
123
+ /**
124
+ * When in debug mode this function will trigger running the background tasks.
125
+ * This function will only work for apps built in debug mode.
126
+ * This method is only available in development mode. It will not work in production builds.
127
+ * @returns A promise which fulfils when the task is triggered.
128
+ */
129
+ export async function triggerTaskWorkerForTestingAsync(): Promise<boolean> {
130
+ if (__DEV__) {
131
+ if (!ExpoBackgroundTaskModule.triggerTaskWorkerForTestingAsync) {
132
+ throw new UnavailabilityError('BackgroundTask', 'triggerTaskWorkerForTestingAsync');
133
+ }
134
+ console.log('Calling triggerTaskWorkerForTestingAsync');
135
+ return await ExpoBackgroundTaskModule.triggerTaskWorkerForTestingAsync();
136
+ } else {
137
+ return Promise.resolve(false);
138
+ }
139
+ }
140
+
141
+ // @needsAudit
142
+ /**
143
+ * Adds a listener that is called when the background executor expires. On iOS, tasks can run
144
+ * for minutes, but the system can interrupt the process at any time. This listener is called
145
+ * when the system decides to stop the background tasks and should be used to clean up resources
146
+ * or save state. When the expiry handler is called, the main task runner is rescheduled automatically.
147
+ * @platform ios
148
+ * @return An object with a `remove` method to unsubscribe the listener.
149
+ */
150
+ export function addExpirationListener(listener: () => void): { remove: () => void } {
151
+ if (!ExpoBackgroundTaskModule.addListener) {
152
+ throw new UnavailabilityError('BackgroundTask', 'addListener');
153
+ }
154
+ return ExpoBackgroundTaskModule.addListener('onTasksExpired', listener);
155
+ }
156
+
157
+ // Export types
158
+ export {
159
+ BackgroundTaskStatus,
160
+ BackgroundTaskResult,
161
+ BackgroundTaskOptions,
162
+ } from './BackgroundTask.types';
@@ -0,0 +1,75 @@
1
+ // @needsAudit
2
+ /**
3
+ * Availability status for background tasks
4
+ */
5
+ export enum BackgroundTaskStatus {
6
+ /**
7
+ * Background tasks are unavailable.
8
+ */
9
+ Restricted = 1,
10
+ /**
11
+ * Background tasks are available for the app.
12
+ */
13
+ Available = 2,
14
+ }
15
+
16
+ // @needsAudit
17
+ /**
18
+ * Return value for background tasks.
19
+ */
20
+ export enum BackgroundTaskResult {
21
+ /**
22
+ * The task finished successfully.
23
+ */
24
+ Success = 1,
25
+ /**
26
+ * The task failed.
27
+ */
28
+ Failed = 2,
29
+ }
30
+
31
+ // @needsAudit
32
+ /**
33
+ * Options for registering a background task
34
+ */
35
+ export type BackgroundTaskOptions = {
36
+ /**
37
+ * Inexact interval in minutes between subsequent repeats of the background tasks. The final
38
+ * interval may differ from the specified one to minimize wakeups and battery usage.
39
+ * - Defaults to once every 12 hours (The minimum interval is 15 minutes)
40
+ * - The system controls the background task execution interval and treats the
41
+ * specified value as a minimum delay. Tasks won't run exactly on schedule. On iOS, short
42
+ * intervals are often ignored—the system typically runs background tasks during
43
+ * specific windows, such as overnight.
44
+ *
45
+ */
46
+ minimumInterval?: number;
47
+ /**
48
+ * Whether the task requires network connectivity.
49
+ *
50
+ * @default true
51
+ * @platform ios, android
52
+ */
53
+ requiresNetworkConnectivity?: boolean;
54
+ /**
55
+ * iOS-only: Which BGTask type to use.
56
+ * - `'refresh'` (default): BGAppRefreshTaskRequest — aggressive scheduling (~15-30 min),
57
+ * ~30 s execution limit. Suitable for periodic syncs and content updates.
58
+ * - `'processing'`: BGProcessingTaskRequest — iOS defers at its own discretion
59
+ * (hours/days), but allows several minutes of runtime. Suitable for heavy work
60
+ * (ML training, DB migration). Supports `requiresNetworkConnectivity` and
61
+ * `requiresExternalPower`.
62
+ *
63
+ * @default 'refresh'
64
+ * @platform ios
65
+ */
66
+ iosTaskType?: 'refresh' | 'processing';
67
+ /**
68
+ * iOS-only: Whether the task requires external power.
69
+ * Only relevant when `iosTaskType: 'processing'`.
70
+ *
71
+ * @default false
72
+ * @platform ios
73
+ */
74
+ requiresExternalPower?: boolean;
75
+ };
@@ -0,0 +1,16 @@
1
+ import { requireNativeModule, type NativeModule } from 'expo';
2
+
3
+ import { BackgroundTaskOptions, BackgroundTaskStatus } from './BackgroundTask.types';
4
+
5
+ type ExpoBackgroundTaskEvents = {
6
+ onTasksExpired(): void;
7
+ };
8
+
9
+ declare class ExpoBackgroundTaskModule extends NativeModule<ExpoBackgroundTaskEvents> {
10
+ getStatusAsync(): Promise<BackgroundTaskStatus>;
11
+ registerTaskAsync(name: string, options: BackgroundTaskOptions): Promise<void>;
12
+ unregisterTaskAsync(name: string): Promise<void>;
13
+ triggerTaskWorkerForTestingAsync(): Promise<boolean>;
14
+ }
15
+
16
+ export default requireNativeModule<ExpoBackgroundTaskModule>('ExpoBackgroundTask');
@@ -0,0 +1,7 @@
1
+ import { BackgroundTaskStatus } from './BackgroundTask.types';
2
+
3
+ export default {
4
+ async getStatusAsync(): Promise<BackgroundTaskStatus> {
5
+ return BackgroundTaskStatus.Restricted;
6
+ },
7
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ // @generated by expo-module-scripts
2
+ {
3
+ "extends": "expo-module-scripts/tsconfig.base",
4
+ "compilerOptions": {
5
+ "outDir": "./build"
6
+ },
7
+ "include": ["./src"],
8
+ "exclude": ["**/__mocks__/*", "**/__tests__/*", "**/__rsc_tests__/*"]
9
+ }