From dd7595b0a6332cc3f94bc5f4e0df2caea8b2663c Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Tue, 6 May 2025 14:16:05 +0200 Subject: [PATCH 1/5] Fallback to old EventEmitter API on Android --- .../ReactNativeBackgroundTaskModule.kt | 13 ++++++++++++- .../src/NativeReactNativeBackgroundTask.ts | 2 -- modules/background-task/src/index.ts | 8 ++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt index 19d3e31d83c17..d6b681a24e868 100644 --- a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt +++ b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt @@ -14,15 +14,26 @@ import android.content.IntentFilter import android.os.Build import android.os.PersistableBundle import android.util.Log +import com.facebook.react.modules.core.DeviceEventManagerModule +import com.facebook.react.bridge.WritableMap +import com.facebook.react.bridge.Arguments class ReactNativeBackgroundTaskModule internal constructor(context: ReactApplicationContext) : ReactNativeBackgroundTaskSpec(context) { + private fun sendEvent(eventName: String, params: WritableMap?) { + reactApplicationContext + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) + .emit(eventName, params) + } + private val taskReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { val taskName = intent?.getStringExtra("taskName") Log.d("ReactNativeBackgroundTaskModule", "Received task: $taskName") - emitOnBackgroundTaskExecution(taskName) + val params = Arguments.createMap() + params.putString("taskName", taskName) + sendEvent("onBackgroundTaskExecution", params) } } diff --git a/modules/background-task/src/NativeReactNativeBackgroundTask.ts b/modules/background-task/src/NativeReactNativeBackgroundTask.ts index 792fe28505522..15aba09bef0cb 100644 --- a/modules/background-task/src/NativeReactNativeBackgroundTask.ts +++ b/modules/background-task/src/NativeReactNativeBackgroundTask.ts @@ -1,12 +1,10 @@ import type {TurboModule} from 'react-native'; import {TurboModuleRegistry} from 'react-native'; -import type {EventEmitter} from 'react-native/Libraries/Types/CodegenTypes'; // We need to export the interface inline for proper TypeScript type inference with TurboModules // eslint-disable-next-line rulesdir/no-inline-named-export, @typescript-eslint/consistent-type-definitions export interface Spec extends TurboModule { defineTask(taskName: string, taskExecutor: (data: unknown) => void | Promise): Promise; - readonly onBackgroundTaskExecution: EventEmitter; } export default TurboModuleRegistry.getEnforcing('ReactNativeBackgroundTask'); diff --git a/modules/background-task/src/index.ts b/modules/background-task/src/index.ts index d3afe75a85bc2..462421ea44e57 100644 --- a/modules/background-task/src/index.ts +++ b/modules/background-task/src/index.ts @@ -1,16 +1,20 @@ +import { DeviceEventEmitter, EmitterSubscription } from 'react-native'; import NativeReactNativeBackgroundTask from './NativeReactNativeBackgroundTask'; type TaskManagerTaskExecutor = (data: T) => void | Promise; const taskExecutors = new Map(); -NativeReactNativeBackgroundTask.onBackgroundTaskExecution((taskName) => { +function onBackgroundTaskExecution({taskName}: {taskName: string}) { + console.log('onBackgroundTaskExecution', taskName); const executor = taskExecutors.get(taskName); if (executor) { executor(taskName); } -}); +} + +const backgroundTasksSubscription = DeviceEventEmitter.addListener('onBackgroundTaskExecution', onBackgroundTaskExecution); const TaskManager = { /** From 5216182e7aea4b2f2a2a041e598aef5ece423341 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Tue, 6 May 2025 17:58:05 +0200 Subject: [PATCH 2/5] Use old API on iOS --- .../ReactNativeBackgroundTaskModule.kt | 8 ++++++++ .../ios/ReactNativeBackgroundTask.h | 5 +++-- .../ios/ReactNativeBackgroundTask.mm | 9 ++++++++- .../src/NativeReactNativeBackgroundTask.ts | 2 ++ modules/background-task/src/index.ts | 14 +++++++++++--- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt index d6b681a24e868..6b0f81c0e92d0 100644 --- a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt +++ b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt @@ -91,6 +91,14 @@ class ReactNativeBackgroundTaskModule internal constructor(context: ReactApplica } } + override fun addListener(eventType: String?) { + // no-op + } + + override fun removeListeners(count: Double) { + // no-op + } + companion object { const val NAME = "ReactNativeBackgroundTask" } diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.h b/modules/background-task/ios/ReactNativeBackgroundTask.h index 8f3f0e114501a..054f1019157d9 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.h +++ b/modules/background-task/ios/ReactNativeBackgroundTask.h @@ -1,13 +1,14 @@ +#import #ifdef RCT_NEW_ARCH_ENABLED #import #import -@interface ReactNativeBackgroundTask : NativeReactNativeBackgroundTaskSpecBase +@interface ReactNativeBackgroundTask : RCTEventEmitter #else #import #import -@interface ReactNativeBackgroundTask : NSObject +@interface ReactNativeBackgroundTask : RCTEventEmitter #endif - (void)defineTask:(NSString *)taskName diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index f963c928a41ed..68f9e4968fc67 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -73,7 +73,7 @@ - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier error:(NSError **)outEr // Execute all registered tasks [self->_taskExecutors enumerateKeysAndObjectsUsingBlock:^(NSString *taskName, RCTResponseSenderBlock executor, BOOL *stop) { NSLog(@"[ReactNativeBackgroundTask] Executing task: %@", taskName); - [self emitOnBackgroundTaskExecution:(taskName)]; + [self sendEventWithName:@"onBackgroundTaskExecution" body:@{@"taskName": taskName}]; }]; NSError *scheduleError = nil; @@ -103,6 +103,13 @@ - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier error:(NSError **)outEr } _taskExecutors[taskName] = taskExecutor; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self sendEventWithName:@"onBackgroundTaskExecution" body:@{@"taskName": taskName}]; + }); +} + +- (NSArray *)supportedEvents { + return @[@"onBackgroundTaskExecution"]; } // Don't compile this code when we build for the old architecture. diff --git a/modules/background-task/src/NativeReactNativeBackgroundTask.ts b/modules/background-task/src/NativeReactNativeBackgroundTask.ts index 15aba09bef0cb..737d783d9634c 100644 --- a/modules/background-task/src/NativeReactNativeBackgroundTask.ts +++ b/modules/background-task/src/NativeReactNativeBackgroundTask.ts @@ -5,6 +5,8 @@ import {TurboModuleRegistry} from 'react-native'; // eslint-disable-next-line rulesdir/no-inline-named-export, @typescript-eslint/consistent-type-definitions export interface Spec extends TurboModule { defineTask(taskName: string, taskExecutor: (data: unknown) => void | Promise): Promise; + addListener: (eventType: string) => void; + removeListeners: (count: number) => void; } export default TurboModuleRegistry.getEnforcing('ReactNativeBackgroundTask'); diff --git a/modules/background-task/src/index.ts b/modules/background-task/src/index.ts index 462421ea44e57..c91c96add6147 100644 --- a/modules/background-task/src/index.ts +++ b/modules/background-task/src/index.ts @@ -1,12 +1,13 @@ -import { DeviceEventEmitter, EmitterSubscription } from 'react-native'; +import { DeviceEventEmitter, EmitterSubscription, NativeEventEmitter } from 'react-native'; import NativeReactNativeBackgroundTask from './NativeReactNativeBackgroundTask'; type TaskManagerTaskExecutor = (data: T) => void | Promise; +const eventEmitter = new NativeEventEmitter(NativeReactNativeBackgroundTask); const taskExecutors = new Map(); +let subscription: EmitterSubscription | undefined; function onBackgroundTaskExecution({taskName}: {taskName: string}) { - console.log('onBackgroundTaskExecution', taskName); const executor = taskExecutors.get(taskName); if (executor) { @@ -14,7 +15,12 @@ function onBackgroundTaskExecution({taskName}: {taskName: string}) { } } -const backgroundTasksSubscription = DeviceEventEmitter.addListener('onBackgroundTaskExecution', onBackgroundTaskExecution); +function addBackgroundTaskListener() { + if (subscription) { + subscription.remove(); + } + subscription = eventEmitter.addListener('onBackgroundTaskExecution', onBackgroundTaskExecution); +} const TaskManager = { /** @@ -36,4 +42,6 @@ const TaskManager = { }, }; +addBackgroundTaskListener(); + export default TaskManager; From c7dc7df995551139840bc764a132b5c2b8eb3b4c Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Tue, 6 May 2025 17:59:21 +0200 Subject: [PATCH 3/5] Remove delayed event --- modules/background-task/ios/ReactNativeBackgroundTask.mm | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index 68f9e4968fc67..861168ae508fc 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -103,9 +103,6 @@ - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier error:(NSError **)outEr } _taskExecutors[taskName] = taskExecutor; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self sendEventWithName:@"onBackgroundTaskExecution" body:@{@"taskName": taskName}]; - }); } - (NSArray *)supportedEvents { From 6ee9ba6cd4ff757cd7ebb3f8a8aa555783e88764 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Wed, 7 May 2025 10:58:19 +0200 Subject: [PATCH 4/5] Fix lint --- modules/background-task/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/background-task/src/index.ts b/modules/background-task/src/index.ts index c91c96add6147..65f55d3741804 100644 --- a/modules/background-task/src/index.ts +++ b/modules/background-task/src/index.ts @@ -1,4 +1,5 @@ -import { DeviceEventEmitter, EmitterSubscription, NativeEventEmitter } from 'react-native'; +import type { EmitterSubscription } from 'react-native'; +import { NativeEventEmitter } from 'react-native'; import NativeReactNativeBackgroundTask from './NativeReactNativeBackgroundTask'; type TaskManagerTaskExecutor = (data: T) => void | Promise; From c8ba52fc9b31aa9bdd5239eadabeca85b5fec603 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Wed, 7 May 2025 11:07:24 +0200 Subject: [PATCH 5/5] Fix prettier --- modules/background-task/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/background-task/src/index.ts b/modules/background-task/src/index.ts index 65f55d3741804..d1254b991eeda 100644 --- a/modules/background-task/src/index.ts +++ b/modules/background-task/src/index.ts @@ -1,5 +1,5 @@ -import type { EmitterSubscription } from 'react-native'; -import { NativeEventEmitter } from 'react-native'; +import type {EmitterSubscription} from 'react-native'; +import {NativeEventEmitter} from 'react-native'; import NativeReactNativeBackgroundTask from './NativeReactNativeBackgroundTask'; type TaskManagerTaskExecutor = (data: T) => void | Promise;