expo-background-task 1.0.7 → 1.0.8

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/CHANGELOG.md +10 -0
  2. package/android/build.gradle +2 -2
  3. package/build/BackgroundTask.d.ts +11 -0
  4. package/build/BackgroundTask.d.ts.map +1 -1
  5. package/build/BackgroundTask.js +15 -0
  6. package/build/BackgroundTask.js.map +1 -1
  7. package/build/ExpoBackgroundTaskModule.d.ts +4 -1
  8. package/build/ExpoBackgroundTaskModule.d.ts.map +1 -1
  9. package/build/ExpoBackgroundTaskModule.js.map +1 -1
  10. package/expo-module.config.json +1 -1
  11. package/ios/BackgroundTaskAppDelegate.swift +19 -10
  12. package/ios/BackgroundTaskModule.swift +29 -3
  13. package/ios/BackgroundTaskScheduler.swift +49 -0
  14. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7.module → 1.0.8/expo.modules.backgroundtask-1.0.8.module} +7 -7
  15. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.8/expo.modules.backgroundtask-1.0.8.module.md5 +1 -0
  16. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.8/expo.modules.backgroundtask-1.0.8.module.sha1 +1 -0
  17. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.8/expo.modules.backgroundtask-1.0.8.module.sha256 +1 -0
  18. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.8/expo.modules.backgroundtask-1.0.8.module.sha512 +1 -0
  19. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7.pom → 1.0.8/expo.modules.backgroundtask-1.0.8.pom} +1 -1
  20. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.8/expo.modules.backgroundtask-1.0.8.pom.md5 +1 -0
  21. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.8/expo.modules.backgroundtask-1.0.8.pom.sha1 +1 -0
  22. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.8/expo.modules.backgroundtask-1.0.8.pom.sha256 +1 -0
  23. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.8/expo.modules.backgroundtask-1.0.8.pom.sha512 +1 -0
  24. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/maven-metadata.xml +4 -4
  25. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/maven-metadata.xml.md5 +1 -1
  26. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/maven-metadata.xml.sha1 +1 -1
  27. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/maven-metadata.xml.sha256 +1 -1
  28. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/maven-metadata.xml.sha512 +1 -1
  29. package/package.json +2 -2
  30. package/src/BackgroundTask.ts +16 -0
  31. package/src/ExpoBackgroundTaskModule.ts +5 -1
  32. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.7/expo.modules.backgroundtask-1.0.7.module.md5 +0 -1
  33. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.7/expo.modules.backgroundtask-1.0.7.module.sha1 +0 -1
  34. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.7/expo.modules.backgroundtask-1.0.7.module.sha256 +0 -1
  35. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.7/expo.modules.backgroundtask-1.0.7.module.sha512 +0 -1
  36. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.7/expo.modules.backgroundtask-1.0.7.pom.md5 +0 -1
  37. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.7/expo.modules.backgroundtask-1.0.7.pom.sha1 +0 -1
  38. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.7/expo.modules.backgroundtask-1.0.7.pom.sha256 +0 -1
  39. package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/1.0.7/expo.modules.backgroundtask-1.0.7.pom.sha512 +0 -1
  40. /package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7-sources.jar → 1.0.8/expo.modules.backgroundtask-1.0.8-sources.jar} +0 -0
  41. /package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7-sources.jar.md5 → 1.0.8/expo.modules.backgroundtask-1.0.8-sources.jar.md5} +0 -0
  42. /package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7-sources.jar.sha1 → 1.0.8/expo.modules.backgroundtask-1.0.8-sources.jar.sha1} +0 -0
  43. /package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7-sources.jar.sha256 → 1.0.8/expo.modules.backgroundtask-1.0.8-sources.jar.sha256} +0 -0
  44. /package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7-sources.jar.sha512 → 1.0.8/expo.modules.backgroundtask-1.0.8-sources.jar.sha512} +0 -0
  45. /package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7.aar → 1.0.8/expo.modules.backgroundtask-1.0.8.aar} +0 -0
  46. /package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7.aar.md5 → 1.0.8/expo.modules.backgroundtask-1.0.8.aar.md5} +0 -0
  47. /package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7.aar.sha1 → 1.0.8/expo.modules.backgroundtask-1.0.8.aar.sha1} +0 -0
  48. /package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7.aar.sha256 → 1.0.8/expo.modules.backgroundtask-1.0.8.aar.sha256} +0 -0
  49. /package/local-maven-repo/host/exp/exponent/expo.modules.backgroundtask/{1.0.7/expo.modules.backgroundtask-1.0.7.aar.sha512 → 1.0.8/expo.modules.backgroundtask-1.0.8.aar.sha512} +0 -0
package/CHANGELOG.md CHANGED
@@ -10,6 +10,16 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 1.0.8 — 2025-09-18
14
+
15
+ ### 🎉 New features
16
+
17
+ - [iOS] Added support for task expiration handler on iOS. ([#39773](https://github.com/expo/expo/pull/39773) by [@chrfalch](https://github.com/chrfalch))
18
+
19
+ ### 🐛 Bug fixes
20
+
21
+ - [iOS] Fixed timing issue where background task iOS handler was not registered when we try to schedule a task. ([#39769](https://github.com/expo/expo/pull/39769) by [@chrfalch](https://github.com/chrfalch))
22
+
13
23
  ## 1.0.7 — 2025-09-11
14
24
 
15
25
  _This version does not introduce any user-facing changes._
@@ -4,7 +4,7 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '1.0.7'
7
+ version = '1.0.8'
8
8
 
9
9
  dependencies {
10
10
  implementation 'androidx.work:work-runtime-ktx:2.9.1'
@@ -15,7 +15,7 @@ android {
15
15
  namespace "expo.modules.backgroundtask"
16
16
  defaultConfig {
17
17
  versionCode 23
18
- versionName "1.0.7"
18
+ versionName "1.0.8"
19
19
  consumerProguardFiles("proguard-rules.pro")
20
20
  }
21
21
  }
@@ -48,5 +48,16 @@ export declare function unregisterTaskAsync(taskName: string): Promise<void>;
48
48
  * @returns A promise which fulfils when the task is triggered.
49
49
  */
50
50
  export declare function triggerTaskWorkerForTestingAsync(): Promise<boolean>;
51
+ /**
52
+ * Adds a listener that is called when the background executor expires. On iOS, tasks can run
53
+ * for minutes, but the system can interrupt the process at any time. This listener is called
54
+ * when the system decides to stop the background tasks and should be used to clean up resources
55
+ * or save state. When the expiry handler is called, the main task runner is rescheduled automatically.
56
+ * @platform ios
57
+ * @return An object with a `remove` method to unsubscribe the listener.
58
+ */
59
+ export declare function addExpirationListener(listener: () => void): {
60
+ remove: () => void;
61
+ };
51
62
  export { BackgroundTaskStatus, BackgroundTaskResult, BackgroundTaskOptions, } from './BackgroundTask.types';
52
63
  //# sourceMappingURL=BackgroundTask.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BackgroundTask.d.ts","sourceRoot":"","sources":["../src/BackgroundTask.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAwBrF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QAAa,OAAO,CAAC,oBAAoB,CAQnE,CAAC;AAGF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAGD;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASzE;AAGD;;;;;GAKG;AACH,wBAAsB,gCAAgC,IAAI,OAAO,CAAC,OAAO,CAAC,CAUzE;AAGD,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"BackgroundTask.d.ts","sourceRoot":"","sources":["../src/BackgroundTask.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAwBrF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QAAa,OAAO,CAAC,oBAAoB,CAQnE,CAAC;AAGF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAGD;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASzE;AAGD;;;;;GAKG;AACH,wBAAsB,gCAAgC,IAAI,OAAO,CAAC,OAAO,CAAC,CAUzE;AAGD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG;IAAE,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,CAKlF;AAGD,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC"}
@@ -121,6 +121,21 @@ export async function triggerTaskWorkerForTestingAsync() {
121
121
  return Promise.resolve(false);
122
122
  }
123
123
  }
124
+ // @needsAudit
125
+ /**
126
+ * Adds a listener that is called when the background executor expires. On iOS, tasks can run
127
+ * for minutes, but the system can interrupt the process at any time. This listener is called
128
+ * when the system decides to stop the background tasks and should be used to clean up resources
129
+ * or save state. When the expiry handler is called, the main task runner is rescheduled automatically.
130
+ * @platform ios
131
+ * @return An object with a `remove` method to unsubscribe the listener.
132
+ */
133
+ export function addExpirationListener(listener) {
134
+ if (!ExpoBackgroundTaskModule.addListener) {
135
+ throw new UnavailabilityError('BackgroundTask', 'addListener');
136
+ }
137
+ return ExpoBackgroundTaskModule.addListener('onTasksExpired', listener);
138
+ }
124
139
  // Export types
125
140
  export { BackgroundTaskStatus, BackgroundTaskResult, } from './BackgroundTask.types';
126
141
  //# sourceMappingURL=BackgroundTask.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BackgroundTask.js","sourceRoot":"","sources":["../src/BackgroundTask.ts"],"names":[],"mappings":"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","sourcesContent":["import { isRunningInExpoGo } from 'expo';\nimport { Platform, UnavailabilityError } from 'expo-modules-core';\nimport * as TaskManager from 'expo-task-manager';\n\nimport { BackgroundTaskOptions, BackgroundTaskStatus } from './BackgroundTask.types';\nimport ExpoBackgroundTaskModule from './ExpoBackgroundTaskModule';\n\n// Flag to warn about running on Apple simulator\nlet warnAboutRunningOniOSSimulator = false;\n\nlet warnedAboutExpoGo = false;\n\nfunction _validate(taskName: unknown) {\n if (isRunningInExpoGo()) {\n if (!warnedAboutExpoGo) {\n const message =\n '`Background Task` functionality is not available in Expo Go:\\n' +\n 'You can use this API and any others in a development build. Learn more: https://expo.fyi/dev-client.';\n console.warn(message);\n warnedAboutExpoGo = true;\n }\n }\n if (!taskName || typeof taskName !== 'string') {\n throw new TypeError('`taskName` must be a non-empty string.');\n }\n}\n\n// @needsAudit\n/**\n * Returns the status for the Background Task API. On web, it always returns `BackgroundTaskStatus.Restricted`,\n * while on native platforms it returns `BackgroundTaskStatus.Available`.\n *\n * @returns A BackgroundTaskStatus enum value or `null` if not available.\n */\nexport const getStatusAsync = async (): Promise<BackgroundTaskStatus> => {\n if (!ExpoBackgroundTaskModule.getStatusAsync) {\n throw new UnavailabilityError('BackgroundTask', 'getStatusAsync');\n }\n\n return isRunningInExpoGo()\n ? BackgroundTaskStatus.Restricted\n : ExpoBackgroundTaskModule.getStatusAsync();\n};\n\n// @needsAudit\n/**\n * Registers a background task with the given name. Registered tasks are saved in persistent storage and restored once the app is initialized.\n * @param taskName Name of the task to register. The task needs to be defined first - see [`TaskManager.defineTask`](task-manager/#taskmanagerdefinetasktaskname-taskexecutor)\n * for more details.\n * @param options An object containing the background task options.\n *\n * @example\n * ```ts\n * import * as TaskManager from 'expo-task-manager';\n *\n * // Register the task outside of the component\n * TaskManager.defineTask(BACKGROUND_TASK_IDENTIFIER, () => {\n * try {\n * await AsyncStorage.setItem(LAST_TASK_DATE_KEY, Date.now().toString());\n * } catch (error) {\n * console.error('Failed to save the last fetch date', error);\n * return BackgroundTaskResult.Failed;\n * }\n * return BackgroundTaskResult.Success;\n * });\n * ```\n *\n * You can now use the `registerTaskAsync` function to register the task:\n *\n * ```ts\n * BackgroundTask.registerTaskAsync(BACKGROUND_TASK_IDENTIFIER, {});\n * ```\n */\nexport async function registerTaskAsync(\n taskName: string,\n options: BackgroundTaskOptions = {}\n): Promise<void> {\n if (!ExpoBackgroundTaskModule.registerTaskAsync) {\n throw new UnavailabilityError('BackgroundTask', 'registerTaskAsync');\n }\n if (!TaskManager.isTaskDefined(taskName)) {\n throw new Error(\n `Task '${taskName}' is not defined. You must define a task using TaskManager.defineTask before registering.`\n );\n }\n\n if ((await ExpoBackgroundTaskModule.getStatusAsync()) === BackgroundTaskStatus.Restricted) {\n if (!warnAboutRunningOniOSSimulator) {\n const message =\n Platform.OS === 'ios'\n ? `Background tasks are not supported on iOS simulators. Skipped registering task: ${taskName}.`\n : `Background tasks are not available in the current environment. Skipped registering task: ${taskName}.`;\n console.warn(message);\n warnAboutRunningOniOSSimulator = true;\n }\n return;\n }\n _validate(taskName);\n if (await TaskManager.isTaskRegisteredAsync(taskName)) {\n return;\n }\n await ExpoBackgroundTaskModule.registerTaskAsync(taskName, options);\n}\n\n// @needsAudit\n/**\n * Unregisters a background task, so the application will no longer be executing this task.\n * @param taskName Name of the task to unregister.\n * @return A promise which fulfils when the task is fully unregistered.\n */\nexport async function unregisterTaskAsync(taskName: string): Promise<void> {\n if (!ExpoBackgroundTaskModule.unregisterTaskAsync) {\n throw new UnavailabilityError('BackgroundTask', 'unregisterTaskAsync');\n }\n _validate(taskName);\n if (!(await TaskManager.isTaskRegisteredAsync(taskName))) {\n return;\n }\n await ExpoBackgroundTaskModule.unregisterTaskAsync(taskName);\n}\n\n// @needsAudit\n/**\n * When in debug mode this function will trigger running the background tasks.\n * This function will only work for apps built in debug mode.\n * This method is only available in development mode. It will not work in production builds.\n * @returns A promise which fulfils when the task is triggered.\n */\nexport async function triggerTaskWorkerForTestingAsync(): Promise<boolean> {\n if (__DEV__) {\n if (!ExpoBackgroundTaskModule.triggerTaskWorkerForTestingAsync) {\n throw new UnavailabilityError('BackgroundTask', 'triggerTaskWorkerForTestingAsync');\n }\n console.log('Calling triggerTaskWorkerForTestingAsync');\n return await ExpoBackgroundTaskModule.triggerTaskWorkerForTestingAsync();\n } else {\n return Promise.resolve(false);\n }\n}\n\n// Export types\nexport {\n BackgroundTaskStatus,\n BackgroundTaskResult,\n BackgroundTaskOptions,\n} from './BackgroundTask.types';\n"]}
1
+ {"version":3,"file":"BackgroundTask.js","sourceRoot":"","sources":["../src/BackgroundTask.ts"],"names":[],"mappings":"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","sourcesContent":["import { isRunningInExpoGo } from 'expo';\nimport { Platform, UnavailabilityError } from 'expo-modules-core';\nimport * as TaskManager from 'expo-task-manager';\n\nimport { BackgroundTaskOptions, BackgroundTaskStatus } from './BackgroundTask.types';\nimport ExpoBackgroundTaskModule from './ExpoBackgroundTaskModule';\n\n// Flag to warn about running on Apple simulator\nlet warnAboutRunningOniOSSimulator = false;\n\nlet warnedAboutExpoGo = false;\n\nfunction _validate(taskName: unknown) {\n if (isRunningInExpoGo()) {\n if (!warnedAboutExpoGo) {\n const message =\n '`Background Task` functionality is not available in Expo Go:\\n' +\n 'You can use this API and any others in a development build. Learn more: https://expo.fyi/dev-client.';\n console.warn(message);\n warnedAboutExpoGo = true;\n }\n }\n if (!taskName || typeof taskName !== 'string') {\n throw new TypeError('`taskName` must be a non-empty string.');\n }\n}\n\n// @needsAudit\n/**\n * Returns the status for the Background Task API. On web, it always returns `BackgroundTaskStatus.Restricted`,\n * while on native platforms it returns `BackgroundTaskStatus.Available`.\n *\n * @returns A BackgroundTaskStatus enum value or `null` if not available.\n */\nexport const getStatusAsync = async (): Promise<BackgroundTaskStatus> => {\n if (!ExpoBackgroundTaskModule.getStatusAsync) {\n throw new UnavailabilityError('BackgroundTask', 'getStatusAsync');\n }\n\n return isRunningInExpoGo()\n ? BackgroundTaskStatus.Restricted\n : ExpoBackgroundTaskModule.getStatusAsync();\n};\n\n// @needsAudit\n/**\n * Registers a background task with the given name. Registered tasks are saved in persistent storage and restored once the app is initialized.\n * @param taskName Name of the task to register. The task needs to be defined first - see [`TaskManager.defineTask`](task-manager/#taskmanagerdefinetasktaskname-taskexecutor)\n * for more details.\n * @param options An object containing the background task options.\n *\n * @example\n * ```ts\n * import * as TaskManager from 'expo-task-manager';\n *\n * // Register the task outside of the component\n * TaskManager.defineTask(BACKGROUND_TASK_IDENTIFIER, () => {\n * try {\n * await AsyncStorage.setItem(LAST_TASK_DATE_KEY, Date.now().toString());\n * } catch (error) {\n * console.error('Failed to save the last fetch date', error);\n * return BackgroundTaskResult.Failed;\n * }\n * return BackgroundTaskResult.Success;\n * });\n * ```\n *\n * You can now use the `registerTaskAsync` function to register the task:\n *\n * ```ts\n * BackgroundTask.registerTaskAsync(BACKGROUND_TASK_IDENTIFIER, {});\n * ```\n */\nexport async function registerTaskAsync(\n taskName: string,\n options: BackgroundTaskOptions = {}\n): Promise<void> {\n if (!ExpoBackgroundTaskModule.registerTaskAsync) {\n throw new UnavailabilityError('BackgroundTask', 'registerTaskAsync');\n }\n if (!TaskManager.isTaskDefined(taskName)) {\n throw new Error(\n `Task '${taskName}' is not defined. You must define a task using TaskManager.defineTask before registering.`\n );\n }\n\n if ((await ExpoBackgroundTaskModule.getStatusAsync()) === BackgroundTaskStatus.Restricted) {\n if (!warnAboutRunningOniOSSimulator) {\n const message =\n Platform.OS === 'ios'\n ? `Background tasks are not supported on iOS simulators. Skipped registering task: ${taskName}.`\n : `Background tasks are not available in the current environment. Skipped registering task: ${taskName}.`;\n console.warn(message);\n warnAboutRunningOniOSSimulator = true;\n }\n return;\n }\n _validate(taskName);\n if (await TaskManager.isTaskRegisteredAsync(taskName)) {\n return;\n }\n await ExpoBackgroundTaskModule.registerTaskAsync(taskName, options);\n}\n\n// @needsAudit\n/**\n * Unregisters a background task, so the application will no longer be executing this task.\n * @param taskName Name of the task to unregister.\n * @return A promise which fulfils when the task is fully unregistered.\n */\nexport async function unregisterTaskAsync(taskName: string): Promise<void> {\n if (!ExpoBackgroundTaskModule.unregisterTaskAsync) {\n throw new UnavailabilityError('BackgroundTask', 'unregisterTaskAsync');\n }\n _validate(taskName);\n if (!(await TaskManager.isTaskRegisteredAsync(taskName))) {\n return;\n }\n await ExpoBackgroundTaskModule.unregisterTaskAsync(taskName);\n}\n\n// @needsAudit\n/**\n * When in debug mode this function will trigger running the background tasks.\n * This function will only work for apps built in debug mode.\n * This method is only available in development mode. It will not work in production builds.\n * @returns A promise which fulfils when the task is triggered.\n */\nexport async function triggerTaskWorkerForTestingAsync(): Promise<boolean> {\n if (__DEV__) {\n if (!ExpoBackgroundTaskModule.triggerTaskWorkerForTestingAsync) {\n throw new UnavailabilityError('BackgroundTask', 'triggerTaskWorkerForTestingAsync');\n }\n console.log('Calling triggerTaskWorkerForTestingAsync');\n return await ExpoBackgroundTaskModule.triggerTaskWorkerForTestingAsync();\n } else {\n return Promise.resolve(false);\n }\n}\n\n// @needsAudit\n/**\n * Adds a listener that is called when the background executor expires. On iOS, tasks can run\n * for minutes, but the system can interrupt the process at any time. This listener is called\n * when the system decides to stop the background tasks and should be used to clean up resources\n * or save state. When the expiry handler is called, the main task runner is rescheduled automatically.\n * @platform ios\n * @return An object with a `remove` method to unsubscribe the listener.\n */\nexport function addExpirationListener(listener: () => void): { remove: () => void } {\n if (!ExpoBackgroundTaskModule.addListener) {\n throw new UnavailabilityError('BackgroundTask', 'addListener');\n }\n return ExpoBackgroundTaskModule.addListener('onTasksExpired', listener);\n}\n\n// Export types\nexport {\n BackgroundTaskStatus,\n BackgroundTaskResult,\n BackgroundTaskOptions,\n} from './BackgroundTask.types';\n"]}
@@ -1,6 +1,9 @@
1
1
  import { type NativeModule } from 'expo';
2
2
  import { BackgroundTaskOptions, BackgroundTaskStatus } from './BackgroundTask.types';
3
- declare class ExpoBackgroundTaskModule extends NativeModule {
3
+ type ExpoBackgroundTaskEvents = {
4
+ onTasksExpired(): void;
5
+ };
6
+ declare class ExpoBackgroundTaskModule extends NativeModule<ExpoBackgroundTaskEvents> {
4
7
  getStatusAsync(): Promise<BackgroundTaskStatus>;
5
8
  registerTaskAsync(name: string, options: BackgroundTaskOptions): Promise<void>;
6
9
  unregisterTaskAsync(name: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoBackgroundTaskModule.d.ts","sourceRoot":"","sources":["../src/ExpoBackgroundTaskModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,MAAM,CAAC;AAE9D,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAErF,OAAO,OAAO,wBAAyB,SAAQ,YAAY;IACzD,cAAc,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAC/C,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAC9E,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAChD,gCAAgC,IAAI,OAAO,CAAC,OAAO,CAAC;CACrD;;AAED,wBAAmF"}
1
+ {"version":3,"file":"ExpoBackgroundTaskModule.d.ts","sourceRoot":"","sources":["../src/ExpoBackgroundTaskModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,MAAM,CAAC;AAE9D,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAErF,KAAK,wBAAwB,GAAG;IAC9B,cAAc,IAAI,IAAI,CAAC;CACxB,CAAC;AAEF,OAAO,OAAO,wBAAyB,SAAQ,YAAY,CAAC,wBAAwB,CAAC;IACnF,cAAc,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAC/C,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAC9E,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAChD,gCAAgC,IAAI,OAAO,CAAC,OAAO,CAAC;CACrD;;AAED,wBAAmF"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoBackgroundTaskModule.js","sourceRoot":"","sources":["../src/ExpoBackgroundTaskModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAqB,MAAM,MAAM,CAAC;AAW9D,eAAe,mBAAmB,CAA2B,oBAAoB,CAAC,CAAC","sourcesContent":["import { requireNativeModule, type NativeModule } from 'expo';\n\nimport { BackgroundTaskOptions, BackgroundTaskStatus } from './BackgroundTask.types';\n\ndeclare class ExpoBackgroundTaskModule extends NativeModule {\n getStatusAsync(): Promise<BackgroundTaskStatus>;\n registerTaskAsync(name: string, options: BackgroundTaskOptions): Promise<void>;\n unregisterTaskAsync(name: string): Promise<void>;\n triggerTaskWorkerForTestingAsync(): Promise<boolean>;\n}\n\nexport default requireNativeModule<ExpoBackgroundTaskModule>('ExpoBackgroundTask');\n"]}
1
+ {"version":3,"file":"ExpoBackgroundTaskModule.js","sourceRoot":"","sources":["../src/ExpoBackgroundTaskModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAqB,MAAM,MAAM,CAAC;AAe9D,eAAe,mBAAmB,CAA2B,oBAAoB,CAAC,CAAC","sourcesContent":["import { requireNativeModule, type NativeModule } from 'expo';\n\nimport { BackgroundTaskOptions, BackgroundTaskStatus } from './BackgroundTask.types';\n\ntype ExpoBackgroundTaskEvents = {\n onTasksExpired(): void;\n};\n\ndeclare class ExpoBackgroundTaskModule extends NativeModule<ExpoBackgroundTaskEvents> {\n getStatusAsync(): Promise<BackgroundTaskStatus>;\n registerTaskAsync(name: string, options: BackgroundTaskOptions): Promise<void>;\n unregisterTaskAsync(name: string): Promise<void>;\n triggerTaskWorkerForTestingAsync(): Promise<boolean>;\n}\n\nexport default requireNativeModule<ExpoBackgroundTaskModule>('ExpoBackgroundTask');\n"]}
@@ -9,7 +9,7 @@
9
9
  "publication": {
10
10
  "groupId": "host.exp.exponent",
11
11
  "artifactId": "expo.modules.backgroundtask",
12
- "version": "1.0.7",
12
+ "version": "1.0.8",
13
13
  "repository": "local-maven-repo"
14
14
  }
15
15
  }
@@ -13,7 +13,10 @@ public class BackgroundTaskAppDelegateSubscriber: ExpoAppDelegateSubscriber {
13
13
  // Set up expiration handler
14
14
  task.expirationHandler = { ()
15
15
  log.warn("Expo Background Tasks - task expired")
16
+ // Send message to Expo module
17
+ NotificationCenter.default.post(name: onTasksExpiredNotification, object: self, userInfo: [:])
16
18
  task.setTaskCompleted(success: false)
19
+ self.reschedule()
17
20
  }
18
21
 
19
22
  // Let's find the task service implementation and call the runTasks(withReason)
@@ -21,24 +24,30 @@ public class BackgroundTaskAppDelegateSubscriber: ExpoAppDelegateSubscriber {
21
24
  taskService.runTasks(with: EXTaskLaunchReasonBackgroundTask, userInfo: nil, completionHandler: { _ in
22
25
  // Mark iOS task as finished - this is important so that we can continue calling it
23
26
  task.setTaskCompleted(success: true)
24
-
25
- // Reschedule
26
- Task {
27
- do {
28
- log.debug("Background task successfully finished. Rescheduling")
29
- try await BackgroundTaskScheduler.tryScheduleWorker()
30
- } catch {
31
- log.error("Could not reschedule the worker after task finished: \(error.localizedDescription)")
32
- }
33
- }
27
+ self.reschedule()
34
28
  })
35
29
  } else {
36
30
  task.setTaskCompleted(success: false)
37
31
  log.error("Expo Background Tasks: Could not find TaskService module")
38
32
  }
39
33
  }
34
+
35
+ // Signal to the scheduler that we're done.
36
+ BackgroundTaskScheduler.bgTaskSchedulerDidFinishRegister()
40
37
  }
41
38
 
42
39
  return true
43
40
  }
41
+
42
+ private func reschedule() {
43
+ // Reschedule
44
+ Task {
45
+ do {
46
+ log.debug("Background task successfully finished. Rescheduling")
47
+ try await BackgroundTaskScheduler.tryScheduleWorker()
48
+ } catch {
49
+ log.error("Could not reschedule the worker after task finished: \(error.localizedDescription)")
50
+ }
51
+ }
52
+ }
44
53
  }
@@ -1,12 +1,30 @@
1
1
  // Copyright 2024-present 650 Industries. All rights reserved.
2
2
  import ExpoModulesCore
3
3
 
4
+ private let onTasksExpired = "onTasksExpired"
5
+ public let onTasksExpiredNotification = Notification.Name(onTasksExpired)
6
+
4
7
  public class BackgroundTaskModule: Module {
5
8
  private var taskManager: EXTaskManagerInterface?
6
9
 
7
10
  public func definition() -> ModuleDefinition {
8
11
  Name("ExpoBackgroundTask")
9
12
 
13
+ Events(onTasksExpired)
14
+
15
+ OnStartObserving(onTasksExpired) {
16
+ NotificationCenter.default.addObserver(
17
+ self,
18
+ selector: #selector(handleTasksExpiredNotification),
19
+ name: onTasksExpiredNotification,
20
+ object: nil)
21
+ }
22
+
23
+ OnStopObserving(onTasksExpired) {
24
+ // swiftlint:disable:next notification_center_detachment
25
+ NotificationCenter.default.removeObserver(self)
26
+ }
27
+
10
28
  OnCreate {
11
29
  taskManager = appContext?.legacyModule(implementing: EXTaskManagerInterface.self)
12
30
  }
@@ -29,7 +47,8 @@ public class BackgroundTaskModule: Module {
29
47
  }
30
48
 
31
49
  // Register task
32
- taskManager.registerTask(withName: name, consumer: BackgroundTaskConsumer.self, options: options)
50
+ taskManager.registerTask(
51
+ withName: name, consumer: BackgroundTaskConsumer.self, options: options)
33
52
  }
34
53
 
35
54
  AsyncFunction("unregisterTaskAsync") { (name: String) in
@@ -51,8 +70,15 @@ public class BackgroundTaskModule: Module {
51
70
  }
52
71
 
53
72
  AsyncFunction("getStatusAsync") {
54
- return BackgroundTaskScheduler.supportsBackgroundTasks() ?
55
- BackgroundTaskStatus.available : .restricted
73
+ return BackgroundTaskScheduler.supportsBackgroundTasks()
74
+ ? BackgroundTaskStatus.available : .restricted
75
+ }
76
+ }
77
+
78
+ @objc func handleTasksExpiredNotification(_ notification: Notification) {
79
+ guard let url = notification.userInfo?["url"] as? URL else {
80
+ return
56
81
  }
82
+ self.sendEvent(onTasksExpired, [:])
57
83
  }
58
84
  }
@@ -13,6 +13,52 @@ public class BackgroundTaskScheduler {
13
13
  */
14
14
  private static var intervalSeconds: TimeInterval = 12 * 60 * 60
15
15
 
16
+ /**
17
+ A one-time async gate that becomes ready after BGTaskScheduler registration finishes.
18
+ Multiple awaiters will be resumed when ready; subsequent awaiters return immediately.
19
+ */
20
+ private actor RegistrationGate {
21
+ private var ready: Bool = false
22
+ private var waiters: [CheckedContinuation<Void, Never>] = []
23
+
24
+ func awaitReady() async {
25
+ if ready { return }
26
+ await withCheckedContinuation { (continuation: CheckedContinuation<Void, Never>) in
27
+ if ready {
28
+ continuation.resume()
29
+ } else {
30
+ waiters.append(continuation)
31
+ }
32
+ }
33
+ }
34
+
35
+ func signalReady() {
36
+ guard !ready else { return }
37
+ ready = true
38
+ let continuations = waiters
39
+ waiters.removeAll()
40
+ for c in continuations {
41
+ c.resume()
42
+ }
43
+ }
44
+ }
45
+
46
+ /**
47
+ Registration gate that will only be signaled when the bgTaskSchedulerDidFinishRegister is called.
48
+ */
49
+ private static let registrationGate = RegistrationGate()
50
+
51
+ /**
52
+ Call from the BackgroundTaskAppDelegate after the call to BGTaskScheduler.shared.register has finished
53
+ so that we can hold back any tryScheduleWorker calls, especially the call to BGTaskScheduler.shared.submit
54
+ that will fail if called before the BGTaskScheduler has successfully registered its handler.
55
+ */
56
+ public static func bgTaskSchedulerDidFinishRegister() {
57
+ Task {
58
+ await registrationGate.signalReady()
59
+ }
60
+ }
61
+
16
62
  /**
17
63
  * Call when a task is registered to keep track of how many background task consumers we have
18
64
  */
@@ -50,6 +96,9 @@ public class BackgroundTaskScheduler {
50
96
  return
51
97
  }
52
98
 
99
+ // Wait until BGTaskScheduler registration has completed.
100
+ await registrationGate.awaitReady()
101
+
53
102
  // Stop existing tasks
54
103
  await stopWorker()
55
104
 
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "host.exp.exponent",
5
5
  "module": "expo.modules.backgroundtask",
6
- "version": "1.0.7",
6
+ "version": "1.0.8",
7
7
  "attributes": {
8
8
  "org.gradle.status": "release"
9
9
  }
@@ -24,8 +24,8 @@
24
24
  },
25
25
  "files": [
26
26
  {
27
- "name": "expo.modules.backgroundtask-1.0.7.aar",
28
- "url": "expo.modules.backgroundtask-1.0.7.aar",
27
+ "name": "expo.modules.backgroundtask-1.0.8.aar",
28
+ "url": "expo.modules.backgroundtask-1.0.8.aar",
29
29
  "size": 48252,
30
30
  "sha512": "5b5e60d520b379283db7486525e8801fa29159482770b20736ce592d9bc9d767c2aeedd2c365a1bc59723340a6064dbc1efc1bbbe64464109e0024b09720a70d",
31
31
  "sha256": "c4f03f70e9596c443a6029c83837e20038469065042559e921d389804a9a1b98",
@@ -64,8 +64,8 @@
64
64
  ],
65
65
  "files": [
66
66
  {
67
- "name": "expo.modules.backgroundtask-1.0.7.aar",
68
- "url": "expo.modules.backgroundtask-1.0.7.aar",
67
+ "name": "expo.modules.backgroundtask-1.0.8.aar",
68
+ "url": "expo.modules.backgroundtask-1.0.8.aar",
69
69
  "size": 48252,
70
70
  "sha512": "5b5e60d520b379283db7486525e8801fa29159482770b20736ce592d9bc9d767c2aeedd2c365a1bc59723340a6064dbc1efc1bbbe64464109e0024b09720a70d",
71
71
  "sha256": "c4f03f70e9596c443a6029c83837e20038469065042559e921d389804a9a1b98",
@@ -84,8 +84,8 @@
84
84
  },
85
85
  "files": [
86
86
  {
87
- "name": "expo.modules.backgroundtask-1.0.7-sources.jar",
88
- "url": "expo.modules.backgroundtask-1.0.7-sources.jar",
87
+ "name": "expo.modules.backgroundtask-1.0.8-sources.jar",
88
+ "url": "expo.modules.backgroundtask-1.0.8-sources.jar",
89
89
  "size": 6850,
90
90
  "sha512": "0fc8a6188061d8818a320cd4c27e02e406cd4bf49aaef15fc95536365ff20a70d5e599ba86698b3b058fa676a7116f5d8530b0d0437eb90092e0d0593edb09bb",
91
91
  "sha256": "b8464c6ac3c14d1f3fed11e436deb971f7203a5389a08fda08951223923d2fca",
@@ -0,0 +1 @@
1
+ 542f501224cc35735103ee3bc89b4bba0cfbeac197cf2e574227c7b96d83a820
@@ -0,0 +1 @@
1
+ 623b51cdad33abc86aba097aa6a5bca40bb12e9ed72e1c11c480b15d9a988523bfaf9eb11f01f9e81086975fcaf8dc70ca6161187354ac05e8205616131781ee
@@ -9,7 +9,7 @@
9
9
  <modelVersion>4.0.0</modelVersion>
10
10
  <groupId>host.exp.exponent</groupId>
11
11
  <artifactId>expo.modules.backgroundtask</artifactId>
12
- <version>1.0.7</version>
12
+ <version>1.0.8</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.backgroundtask</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -0,0 +1 @@
1
+ dfc4e72a1a499bb0676583deedc3b7932c94ccdcece276b0c5bd6cfa70ef9fa3
@@ -0,0 +1 @@
1
+ 43a0a0ad207f00f709262c53a75ebc7008bbb4a7ce08f98b5a6c67742dc561e28180ec5b1d9a041018eb3d22515b3d815be966e1e1e48d55de56c178caa13d23
@@ -3,11 +3,11 @@
3
3
  <groupId>host.exp.exponent</groupId>
4
4
  <artifactId>expo.modules.backgroundtask</artifactId>
5
5
  <versioning>
6
- <latest>1.0.7</latest>
7
- <release>1.0.7</release>
6
+ <latest>1.0.8</latest>
7
+ <release>1.0.8</release>
8
8
  <versions>
9
- <version>1.0.7</version>
9
+ <version>1.0.8</version>
10
10
  </versions>
11
- <lastUpdated>20250911202148</lastUpdated>
11
+ <lastUpdated>20250918213206</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- d2eba3ff1fbbc499f4c8a9b66163d36c
1
+ eda7ff3e888b83b1e9f7fd0e0a265b59
@@ -1 +1 @@
1
- ba27b549c28ffb6d2c40b96fd186dc1ced1a3aa3
1
+ d0f0b61438b5a4511ea4327cbd8b4aca4239b3eb
@@ -1 +1 @@
1
- 6b9f9f2454157b76c707c7b0fd33dc55ed3ecd5b17aee020ea43962ff36ca7cf
1
+ a72fdc74f6d5af6ef96c952ae5c06c20aebc70bd1582fc63802c9ec6568d906c
@@ -1 +1 @@
1
- 6e5268607cc81b69decddec402e2fa1b388c016b033508cee2d223913b97904c83a7772b45758846a0107830f03c00f2515b7ed0af639c0107ba2607c943187a
1
+ dc0108fbcddcfb82815b96075716dc965a1f63974b3f37250067e9e715c71e103ab7b29feb117da17dc70680508f26e86fa379061304460f8348f8bae07d7ec6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-background-task",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Expo Android and iOS module for Background Task APIs",
5
5
  "main": "build/BackgroundTask.js",
6
6
  "types": "build/BackgroundTask.d.ts",
@@ -40,5 +40,5 @@
40
40
  "peerDependencies": {
41
41
  "expo": "*"
42
42
  },
43
- "gitHead": "088e79428be97cf3ee11fc93e0e5a1fc1c8bea1e"
43
+ "gitHead": "0d9ae61f3dea2e2b854576859e5b50fca5503fc1"
44
44
  }
@@ -138,6 +138,22 @@ export async function triggerTaskWorkerForTestingAsync(): Promise<boolean> {
138
138
  }
139
139
  }
140
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
+
141
157
  // Export types
142
158
  export {
143
159
  BackgroundTaskStatus,
@@ -2,7 +2,11 @@ import { requireNativeModule, type NativeModule } from 'expo';
2
2
 
3
3
  import { BackgroundTaskOptions, BackgroundTaskStatus } from './BackgroundTask.types';
4
4
 
5
- declare class ExpoBackgroundTaskModule extends NativeModule {
5
+ type ExpoBackgroundTaskEvents = {
6
+ onTasksExpired(): void;
7
+ };
8
+
9
+ declare class ExpoBackgroundTaskModule extends NativeModule<ExpoBackgroundTaskEvents> {
6
10
  getStatusAsync(): Promise<BackgroundTaskStatus>;
7
11
  registerTaskAsync(name: string, options: BackgroundTaskOptions): Promise<void>;
8
12
  unregisterTaskAsync(name: string): Promise<void>;
@@ -1 +0,0 @@
1
- 4fe0637ff751d0cfb961905d65b93dc53245253613c15193ec49fbe3ada4049d
@@ -1 +0,0 @@
1
- cd78298fdf20d5a3f4cc1f18c23324f6ce60917bbb87d33a63470102de1ca4e5a143232c39b009ba93937e6893e3296f95a1dd9a861d425ed0010c15527158e4
@@ -1 +0,0 @@
1
- cc0b38c4ebc65f6031bcb07677149d2692be02b08bbea0170693e67d78a8663d
@@ -1 +0,0 @@
1
- bd2ab4b781b0c6833e79ca9196c3494d6488241b9564959084365de05ee74612e448ff8902fbcd5af43d6df4114427874cf44f8d4e2da8b53a6d2d7ac7d2d844