From e7ca379f714a27e776fe5891caeb61946bfc4eef Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 20 Nov 2024 12:23:44 +0100 Subject: [PATCH 01/33] feat: init local library --- ios/tmp.xcconfig | 12 ++ modules/background-task/android/build.gradle | 126 ++++++++++++++++++ .../background-task/android/gradle.properties | 5 + .../android/src/main/AndroidManifest.xml | 3 + .../android/src/main/AndroidManifestNew.xml | 2 + .../ReactNativeBackgroundTaskModule.kt | 24 ++++ .../ReactNativeBackgroundTaskPackage.kt | 35 +++++ .../newarch/ReactNativeBackgroundTaskSpec.kt | 7 + .../oldarch/ReactNativeBackgroundTaskSpec.kt | 11 ++ ...nsify-react-native-background-task.podspec | 41 ++++++ .../ios/ReactNativeBackgroundTask.h | 12 ++ .../ios/ReactNativeBackgroundTask.mm | 27 ++++ modules/background-task/package.json | 19 +++ .../background-task/react-native.config.js | 12 ++ .../src/NativeReactNativeBackgroundTask.ts | 8 ++ modules/background-task/src/index.tsx | 29 ++++ package.json | 3 +- 17 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 ios/tmp.xcconfig create mode 100644 modules/background-task/android/build.gradle create mode 100644 modules/background-task/android/gradle.properties create mode 100644 modules/background-task/android/src/main/AndroidManifest.xml create mode 100644 modules/background-task/android/src/main/AndroidManifestNew.xml create mode 100644 modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt create mode 100644 modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt create mode 100644 modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt create mode 100644 modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt create mode 100644 modules/background-task/expensify-react-native-background-task.podspec create mode 100644 modules/background-task/ios/ReactNativeBackgroundTask.h create mode 100644 modules/background-task/ios/ReactNativeBackgroundTask.mm create mode 100644 modules/background-task/package.json create mode 100644 modules/background-task/react-native.config.js create mode 100644 modules/background-task/src/NativeReactNativeBackgroundTask.ts create mode 100644 modules/background-task/src/index.tsx diff --git a/ios/tmp.xcconfig b/ios/tmp.xcconfig new file mode 100644 index 000000000000..ee98b7b0bd8c --- /dev/null +++ b/ios/tmp.xcconfig @@ -0,0 +1,12 @@ +NEW_EXPENSIFY_URL=https:/$()/new.expensify.com/ +SECURE_EXPENSIFY_URL=https:/$()/secure.expensify.com/ +EXPENSIFY_URL=https:/$()/www.expensify.com/ +EXPENSIFY_PARTNER_NAME=chat-expensify-com +EXPENSIFY_PARTNER_PASSWORD=e21965746fd75f82bb66 +PUSHER_APP_KEY=268df511a204fbb60884 +USE_WEB_PROXY=false +ENVIRONMENT=production +SEND_CRASH_REPORTS=true +FB_API_KEY=AIzaSyBrLKgCuo6Vem6Xi5RPokdumssW8HaWBow +FB_APP_ID=1:1008697809946:web:08de4ecb7656b7235445a3 +FB_PROJECT_ID=expensify-mobile-app diff --git a/modules/background-task/android/build.gradle b/modules/background-task/android/build.gradle new file mode 100644 index 000000000000..335b1fa6bde2 --- /dev/null +++ b/modules/background-task/android/build.gradle @@ -0,0 +1,126 @@ +buildscript { + // Buildscript is evaluated before everything else so we can't use getExtOrDefault + def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["ReactNativeBackgroundTask_kotlinVersion"] + + repositories { + google() + mavenCentral() + } + + dependencies { + classpath "com.android.tools.build:gradle:7.2.1" + // noinspection DifferentKotlinGradleVersion + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +def reactNativeArchitectures() { + def value = rootProject.getProperties().get("reactNativeArchitectures") + return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] +} + +def isNewArchitectureEnabled() { + return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" +} + +apply plugin: "com.android.library" +apply plugin: "kotlin-android" + +if (isNewArchitectureEnabled()) { + apply plugin: "com.facebook.react" +} + +def getExtOrDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["ReactNativeBackgroundTask_" + name] +} + +def getExtOrIntegerDefault(name) { + return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["ReactNativeBackgroundTask_" + name]).toInteger() +} + +def supportsNamespace() { + def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.') + def major = parsed[0].toInteger() + def minor = parsed[1].toInteger() + + // Namespace support was added in 7.3.0 + return (major == 7 && minor >= 3) || major >= 8 +} + +android { + if (supportsNamespace()) { + namespace "com.expensify.reactnativebackgroundtask" + + sourceSets { + main { + manifest.srcFile "src/main/AndroidManifestNew.xml" + } + } + } + + compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") + + defaultConfig { + minSdkVersion getExtOrIntegerDefault("minSdkVersion") + targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + + } + + buildFeatures { + buildConfig true + } + + buildTypes { + release { + minifyEnabled false + } + } + + lintOptions { + disable "GradleCompatible" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + sourceSets { + main { + if (isNewArchitectureEnabled()) { + java.srcDirs += [ + "src/newarch", + // Codegen specs + "generated/java", + "generated/jni" + ] + } else { + java.srcDirs += ["src/oldarch"] + } + } + } +} + +repositories { + mavenCentral() + google() +} + +def kotlin_version = getExtOrDefault("kotlinVersion") + +dependencies { + // For < 0.71, this will be from the local maven repo + // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin + //noinspection GradleDynamicVersion + implementation "com.facebook.react:react-native:+" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" +} + +if (isNewArchitectureEnabled()) { + react { + jsRootDir = file("../src/") + libraryName = "ReactNativeBackgroundTask" + codegenJavaPackageName = "com.expensify.reactnativebackgroundtask" + } +} diff --git a/modules/background-task/android/gradle.properties b/modules/background-task/android/gradle.properties new file mode 100644 index 000000000000..6ed0dc89b17c --- /dev/null +++ b/modules/background-task/android/gradle.properties @@ -0,0 +1,5 @@ +ReactNativeBackgroundTask_kotlinVersion=1.7.0 +ReactNativeBackgroundTask_minSdkVersion=21 +ReactNativeBackgroundTask_targetSdkVersion=31 +ReactNativeBackgroundTask_compileSdkVersion=31 +ReactNativeBackgroundTask_ndkversion=21.4.7075529 diff --git a/modules/background-task/android/src/main/AndroidManifest.xml b/modules/background-task/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..327870652628 --- /dev/null +++ b/modules/background-task/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/modules/background-task/android/src/main/AndroidManifestNew.xml b/modules/background-task/android/src/main/AndroidManifestNew.xml new file mode 100644 index 000000000000..a2f47b6057db --- /dev/null +++ b/modules/background-task/android/src/main/AndroidManifestNew.xml @@ -0,0 +1,2 @@ + + 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 new file mode 100644 index 000000000000..c985b33aa800 --- /dev/null +++ b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt @@ -0,0 +1,24 @@ +package com.expensify.reactnativebackgroundtask + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.Promise + +class ReactNativeBackgroundTaskModule internal constructor(context: ReactApplicationContext) : + ReactNativeBackgroundTaskSpec(context) { + + override fun getName(): String { + return NAME + } + + // Example method + // See https://reactnative.dev/docs/native-modules-android + @ReactMethod + override fun multiply(a: Double, b: Double, promise: Promise) { + promise.resolve(a * b) + } + + companion object { + const val NAME = "ReactNativeBackgroundTask" + } +} diff --git a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt new file mode 100644 index 000000000000..dd0547f7ef35 --- /dev/null +++ b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt @@ -0,0 +1,35 @@ +package com.expensify.reactnativebackgroundtask + +import com.facebook.react.TurboReactPackage +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.NativeModule +import com.facebook.react.module.model.ReactModuleInfoProvider +import com.facebook.react.module.model.ReactModuleInfo +import java.util.HashMap + +class ReactNativeBackgroundTaskPackage : TurboReactPackage() { + override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { + return if (name == ReactNativeBackgroundTaskModule.NAME) { + ReactNativeBackgroundTaskModule(reactContext) + } else { + null + } + } + + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { + return ReactModuleInfoProvider { + val moduleInfos: MutableMap = HashMap() + val isTurboModule: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED + moduleInfos[ReactNativeBackgroundTaskModule.NAME] = ReactModuleInfo( + ReactNativeBackgroundTaskModule.NAME, + ReactNativeBackgroundTaskModule.NAME, + false, // canOverrideExistingModule + false, // needsEagerInit + true, // hasConstants + false, // isCxxModule + isTurboModule // isTurboModule + ) + moduleInfos + } + } +} diff --git a/modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt b/modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt new file mode 100644 index 000000000000..2ee39c49c66d --- /dev/null +++ b/modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt @@ -0,0 +1,7 @@ +package com.expensify.reactnativebackgroundtask + +import com.facebook.react.bridge.ReactApplicationContext + +abstract class ReactNativeBackgroundTaskSpec internal constructor(context: ReactApplicationContext) : + NativeReactNativeBackgroundTaskSpec(context) { +} diff --git a/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt b/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt new file mode 100644 index 000000000000..771e6a13734b --- /dev/null +++ b/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt @@ -0,0 +1,11 @@ +package com.expensify.reactnativebackgroundtask + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.Promise + +abstract class ReactNativeBackgroundTaskSpec internal constructor(context: ReactApplicationContext) : + ReactContextBaseJavaModule(context) { + + abstract fun multiply(a: Double, b: Double, promise: Promise) +} diff --git a/modules/background-task/expensify-react-native-background-task.podspec b/modules/background-task/expensify-react-native-background-task.podspec new file mode 100644 index 000000000000..d66739edb3e5 --- /dev/null +++ b/modules/background-task/expensify-react-native-background-task.podspec @@ -0,0 +1,41 @@ +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "package.json"))) +folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' + +Pod::Spec.new do |s| + s.name = "expensify-react-native-background-task" + s.version = package["version"] + s.summary = package["description"] + s.homepage = package["homepage"] + s.license = package["license"] + s.authors = package["author"] + + s.platforms = { :ios => min_ios_version_supported } + s.source = { :git => ".git", :tag => "#{s.version}" } + + s.source_files = "ios/**/*.{h,m,mm,cpp}" + + # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. + # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. + if respond_to?(:install_modules_dependencies, true) + install_modules_dependencies(s) + else + s.dependency "React-Core" + + # Don't install the dependencies when we run `pod install` in the old architecture. + if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then + s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" + s.pod_target_xcconfig = { + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", + "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" + } + s.dependency "React-Codegen" + s.dependency "RCT-Folly" + s.dependency "RCTRequired" + s.dependency "RCTTypeSafety" + s.dependency "ReactCommon/turbomodule/core" + end + end +end diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.h b/modules/background-task/ios/ReactNativeBackgroundTask.h new file mode 100644 index 000000000000..6e0ac781afd8 --- /dev/null +++ b/modules/background-task/ios/ReactNativeBackgroundTask.h @@ -0,0 +1,12 @@ + +#ifdef RCT_NEW_ARCH_ENABLED +#import "RNReactNativeBackgroundTaskSpec.h" + +@interface ReactNativeBackgroundTask : NSObject +#else +#import + +@interface ReactNativeBackgroundTask : NSObject +#endif + +@end diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm new file mode 100644 index 000000000000..2f318ca219dd --- /dev/null +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -0,0 +1,27 @@ +#import "ReactNativeBackgroundTask.h" + +@implementation ReactNativeBackgroundTask +RCT_EXPORT_MODULE() + +// Example method +// See // https://reactnative.dev/docs/native-modules-ios +RCT_EXPORT_METHOD(multiply:(double)a + b:(double)b + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) +{ + NSNumber *result = @(a * b); + + resolve(result); +} + +// Don't compile this code when we build for the old architecture. +#ifdef RCT_NEW_ARCH_ENABLED +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params +{ + return std::make_shared(params); +} +#endif + +@end diff --git a/modules/background-task/package.json b/modules/background-task/package.json new file mode 100644 index 000000000000..92d464777bae --- /dev/null +++ b/modules/background-task/package.json @@ -0,0 +1,19 @@ +{ + "name": "@expensify/react-native-background-task", + "version": "0.0.0", + "description": "Execute tasks in background", + "main": "src/index", + "codegenConfig": { + "name": "RNReactNativeBackgroundTaskSpec", + "type": "modules", + "jsSrcsDir": "src" + }, + "author": " <> ()", + "license": "UNLICENSED", + "homepage": "#readme", + "create-react-native-library": { + "type": "module-mixed", + "languages": "kotlin-objc", + "version": "0.44.1" + } +} diff --git a/modules/background-task/react-native.config.js b/modules/background-task/react-native.config.js new file mode 100644 index 000000000000..66211dab797e --- /dev/null +++ b/modules/background-task/react-native.config.js @@ -0,0 +1,12 @@ +/** + * @type {import('@react-native-community/cli-types').UserDependencyConfig} + */ +module.exports = { + dependency: { + platforms: { + android: { + cmakeListsPath: 'build/generated/source/codegen/jni/CMakeLists.txt', + }, + }, + }, +}; diff --git a/modules/background-task/src/NativeReactNativeBackgroundTask.ts b/modules/background-task/src/NativeReactNativeBackgroundTask.ts new file mode 100644 index 000000000000..c0dad8a46b4e --- /dev/null +++ b/modules/background-task/src/NativeReactNativeBackgroundTask.ts @@ -0,0 +1,8 @@ +import type { TurboModule } from 'react-native'; +import { TurboModuleRegistry } from 'react-native'; + +export interface Spec extends TurboModule { + multiply(a: number, b: number): Promise; +} + +export default TurboModuleRegistry.getEnforcing('ReactNativeBackgroundTask'); diff --git a/modules/background-task/src/index.tsx b/modules/background-task/src/index.tsx new file mode 100644 index 000000000000..090c25dfcdc1 --- /dev/null +++ b/modules/background-task/src/index.tsx @@ -0,0 +1,29 @@ +import { NativeModules, Platform } from 'react-native'; + +const LINKING_ERROR = + `The package '@expensify/react-native-background-task' doesn't seem to be linked. Make sure: \n\n` + + Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) + + '- You rebuilt the app after installing the package\n' + + '- You are not using Expo Go\n'; + +// @ts-expect-error +const isTurboModuleEnabled = global.__turboModuleProxy != null; + +const ReactNativeBackgroundTaskModule = isTurboModuleEnabled + ? require('./NativeReactNativeBackgroundTask').default + : NativeModules.ReactNativeBackgroundTask; + +const ReactNativeBackgroundTask = ReactNativeBackgroundTaskModule + ? ReactNativeBackgroundTaskModule + : new Proxy( + {}, + { + get() { + throw new Error(LINKING_ERROR); + }, + } + ); + +export function multiply(a: number, b: number): Promise { + return ReactNativeBackgroundTask.multiply(a, b); +} diff --git a/package.json b/package.json index 0a12899cccd5..57940d641c06 100644 --- a/package.json +++ b/package.json @@ -188,7 +188,8 @@ "react-plaid-link": "3.3.2", "react-web-config": "^1.0.0", "react-webcam": "^7.1.1", - "react-window": "^1.8.9" + "react-window": "^1.8.9", + "@expensify/react-native-background-task": "file:./modules/background-task" }, "devDependencies": { "@actions/core": "1.10.0", From 82ce553a45c33430d3d3007dd248afde947b6ac3 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 20 Nov 2024 12:57:28 +0100 Subject: [PATCH 02/33] feat: add `defineTask` method --- .../ios/ReactNativeBackgroundTask.h | 6 +++- .../ios/ReactNativeBackgroundTask.mm | 34 +++++++++++++++---- .../src/NativeReactNativeBackgroundTask.ts | 6 ++-- modules/background-task/src/index.ts | 24 +++++++++++++ modules/background-task/src/index.tsx | 29 ---------------- src/App.tsx | 8 ++++- src/setup/backgroundTask/index.native.ts | 20 +++++++++++ src/setup/backgroundTask/index.ts | 3 ++ 8 files changed, 89 insertions(+), 41 deletions(-) create mode 100644 modules/background-task/src/index.ts delete mode 100644 modules/background-task/src/index.tsx create mode 100644 src/setup/backgroundTask/index.native.ts create mode 100644 src/setup/backgroundTask/index.ts diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.h b/modules/background-task/ios/ReactNativeBackgroundTask.h index 6e0ac781afd8..bd5b425a3b5b 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.h +++ b/modules/background-task/ios/ReactNativeBackgroundTask.h @@ -1,4 +1,3 @@ - #ifdef RCT_NEW_ARCH_ENABLED #import "RNReactNativeBackgroundTaskSpec.h" @@ -9,4 +8,9 @@ @interface ReactNativeBackgroundTask : NSObject #endif +- (void)defineTask:(NSString *)taskName + taskExecutor:(RCTResponseSenderBlock)taskExecutor + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + @end diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index 2f318ca219dd..6294be9777ad 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -1,18 +1,38 @@ #import "ReactNativeBackgroundTask.h" -@implementation ReactNativeBackgroundTask +@implementation ReactNativeBackgroundTask { + NSMutableDictionary *_taskExecutors; +} + RCT_EXPORT_MODULE() -// Example method -// See // https://reactnative.dev/docs/native-modules-ios -RCT_EXPORT_METHOD(multiply:(double)a - b:(double)b +- (instancetype)init { + if (self = [super init]) { + _taskExecutors = [NSMutableDictionary new]; + } + return self; +} + +RCT_EXPORT_METHOD(defineTask:(NSString *)taskName + taskExecutor:(RCTResponseSenderBlock)taskExecutor resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { - NSNumber *result = @(a * b); + if (taskName == nil) { + NSLog(@"[ReactNativeBackgroundTask] Failed to define task: taskName is nil"); + reject(@"ERR_INVALID_TASK_NAME", @"Task name must be provided", nil); + return; + } + + if (taskExecutor == nil) { + NSLog(@"[ReactNativeBackgroundTask] Failed to define task: taskExecutor is nil"); + reject(@"ERR_INVALID_TASK_EXECUTOR", @"Task executor must be provided", nil); + return; + } - resolve(result); + NSLog(@"[ReactNativeBackgroundTask] Defining task: %@", taskName); + _taskExecutors[taskName] = taskExecutor; + resolve(nil); } // 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 c0dad8a46b4e..ed2a381a4862 100644 --- a/modules/background-task/src/NativeReactNativeBackgroundTask.ts +++ b/modules/background-task/src/NativeReactNativeBackgroundTask.ts @@ -1,8 +1,8 @@ -import type { TurboModule } from 'react-native'; -import { TurboModuleRegistry } from 'react-native'; +import type {TurboModule} from 'react-native'; +import {TurboModuleRegistry} from 'react-native'; export interface Spec extends TurboModule { - multiply(a: number, b: number): Promise; + defineTask(taskName: string, taskExecutor: (data: unknown) => void | Promise): Promise; } export default TurboModuleRegistry.getEnforcing('ReactNativeBackgroundTask'); diff --git a/modules/background-task/src/index.ts b/modules/background-task/src/index.ts new file mode 100644 index 000000000000..bd764e6c779c --- /dev/null +++ b/modules/background-task/src/index.ts @@ -0,0 +1,24 @@ +import NativeReactNativeBackgroundTask from './NativeReactNativeBackgroundTask'; + +type TaskManagerTaskExecutor = (data: T) => void | Promise; + +const TaskManager = { + /** + * Defines a task that can be executed in the background. + * @param taskName - Name of the task. Must be unique and match the name used when registering the task. + * @param taskExecutor - Function that will be executed when the task runs. + */ + defineTask: (taskName: string, taskExecutor: TaskManagerTaskExecutor): Promise => { + if (typeof taskName !== 'string' || taskName.length === 0) { + throw new Error('Task name must be a string'); + } + if (typeof taskExecutor !== 'function') { + throw new Error('Task executor must be a function'); + } + + console.log('native task manager', NativeReactNativeBackgroundTask); + return NativeReactNativeBackgroundTask.defineTask(taskName, taskExecutor); + }, +}; + +export default TaskManager; diff --git a/modules/background-task/src/index.tsx b/modules/background-task/src/index.tsx deleted file mode 100644 index 090c25dfcdc1..000000000000 --- a/modules/background-task/src/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { NativeModules, Platform } from 'react-native'; - -const LINKING_ERROR = - `The package '@expensify/react-native-background-task' doesn't seem to be linked. Make sure: \n\n` + - Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) + - '- You rebuilt the app after installing the package\n' + - '- You are not using Expo Go\n'; - -// @ts-expect-error -const isTurboModuleEnabled = global.__turboModuleProxy != null; - -const ReactNativeBackgroundTaskModule = isTurboModuleEnabled - ? require('./NativeReactNativeBackgroundTask').default - : NativeModules.ReactNativeBackgroundTask; - -const ReactNativeBackgroundTask = ReactNativeBackgroundTaskModule - ? ReactNativeBackgroundTaskModule - : new Proxy( - {}, - { - get() { - throw new Error(LINKING_ERROR); - }, - } - ); - -export function multiply(a: number, b: number): Promise { - return ReactNativeBackgroundTask.multiply(a, b); -} diff --git a/src/App.tsx b/src/App.tsx index e11592609350..ad04b45a1dfa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import {PortalProvider} from '@gorhom/portal'; -import React from 'react'; +import React, {useEffect} from 'react'; import {LogBox} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import {PickerStateProvider} from 'react-native-picker-select'; @@ -40,6 +40,7 @@ import {ReportIDsContextProvider} from './hooks/useReportIDs'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext'; import type {Route} from './ROUTES'; +import registerBackgroundFetch from './setup/backgroundTask'; import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; type AppProps = { @@ -64,6 +65,11 @@ function App({url}: AppProps) { useDefaultDragAndDrop(); OnyxUpdateManager(); + // TODO: move to correct place + useEffect(() => { + registerBackgroundFetch(); + }, []); + return ( diff --git a/src/setup/backgroundTask/index.native.ts b/src/setup/backgroundTask/index.native.ts new file mode 100644 index 000000000000..9ac706ad3e22 --- /dev/null +++ b/src/setup/backgroundTask/index.native.ts @@ -0,0 +1,20 @@ +// import * as BackgroundFetch from 'expo-background-fetch'; +// import {defineTask} from 'expo-task-manager'; +import TaskManager from '@expensify/react-native-background-task'; +import * as SequentialQueue from '@libs/Network/SequentialQueue'; + +const BACKGROUND_FETCH_TASK = 'FLUSH-SEQUENTIAL-QUEUE-BACKGROUND-FETCH'; + +TaskManager.defineTask(BACKGROUND_FETCH_TASK, () => { + SequentialQueue.flush(); +}); + +function registerBackgroundFetch() { + // return BackgroundFetch.registerTaskAsync(BACKGROUND_FETCH_TASK, { + // minimumInterval: 60 * 15, // 15 minutes + // stopOnTerminate: false, + // startOnBoot: true, + // }); +} + +export default registerBackgroundFetch; diff --git a/src/setup/backgroundTask/index.ts b/src/setup/backgroundTask/index.ts new file mode 100644 index 000000000000..9c1b88dfd8e7 --- /dev/null +++ b/src/setup/backgroundTask/index.ts @@ -0,0 +1,3 @@ +function registerBackgroundFetch() {} + +export default registerBackgroundFetch; From 4c96c52e3fdd5e2baa0c3ea15cdc3a28aa168026 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 20 Nov 2024 15:34:13 +0100 Subject: [PATCH 03/33] feat: use BGTaskScheduler api --- ios/NewExpensify/Info.plist | 2 + ios/Podfile | 1 - .../ios/ReactNativeBackgroundTask.h | 5 ++ .../ios/ReactNativeBackgroundTask.mm | 51 +++++++++++++++++++ modules/background-task/src/index.ts | 1 - src/setup/backgroundTask/index.native.ts | 1 + 6 files changed, 59 insertions(+), 2 deletions(-) diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 8fc94ef68584..318fc3759167 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -97,6 +97,8 @@ UIBackgroundModes remote-notification + fetch + processing UIFileSharingEnabled diff --git a/ios/Podfile b/ios/Podfile index b74584b839b4..e33b15a0ba3c 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -89,7 +89,6 @@ target 'NewExpensify' do :app_path => "#{Pod::Config.instance.installation_root}/.." ) - target 'NewExpensifyTests' do inherit! :complete # Pods for testing diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.h b/modules/background-task/ios/ReactNativeBackgroundTask.h index bd5b425a3b5b..34070299740a 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.h +++ b/modules/background-task/ios/ReactNativeBackgroundTask.h @@ -1,9 +1,11 @@ #ifdef RCT_NEW_ARCH_ENABLED #import "RNReactNativeBackgroundTaskSpec.h" +#import @interface ReactNativeBackgroundTask : NSObject #else #import +#import @interface ReactNativeBackgroundTask : NSObject #endif @@ -13,4 +15,7 @@ resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject; +- (void)handleAppDidFinishLaunching:(NSNotification *)notification; +- (void)handleBackgroundTask:(BGTask *)task API_AVAILABLE(ios(13.0)); + @end diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index 6294be9777ad..601cfa268ae4 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -1,4 +1,6 @@ #import "ReactNativeBackgroundTask.h" +#import +#import @implementation ReactNativeBackgroundTask { NSMutableDictionary *_taskExecutors; @@ -9,10 +11,59 @@ @implementation ReactNativeBackgroundTask { - (instancetype)init { if (self = [super init]) { _taskExecutors = [NSMutableDictionary new]; + + // Add observer in the next run loop to ensure proper registration + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleAppDidFinishLaunching:) + name:UIApplicationDidFinishLaunchingNotification + object:nil]; + }); } return self; } +// Add dealloc to properly remove the observer +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)handleAppDidFinishLaunching:(NSNotification *)notification { + NSLog(@"[ReactNativeBackgroundTask] Registering background task handler"); + + if (@available(iOS 13.0, *)) { + [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:@"com.szymonrybczak.chat" + usingQueue:dispatch_get_main_queue() + launchHandler:^(__kindof BGTask * _Nonnull task) { + [self handleBackgroundTask:task]; + }]; + } +} + +- (void)handleBackgroundTask:(BGTask *)task API_AVAILABLE(ios(13.0)) { + // Create a task request to schedule the next background task + BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:@"com.szymonrybczak.chat"]; + request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15 * 60]; // Schedule for 15 minutes from now + + NSError *error = nil; + if (![[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error]) { + NSLog(@"[ReactNativeBackgroundTask] Failed to schedule next task: %@", error); + } + + // Execute all registered tasks + [_taskExecutors enumerateKeysAndObjectsUsingBlock:^(NSString *taskName, RCTResponseSenderBlock executor, BOOL *stop) { + NSLog(@"[ReactNativeBackgroundTask] Executing background task: %@", taskName); + executor(@[@{ + @"taskName": taskName, + @"type": @"background", + @"identifier": task.identifier + }]); + }]; + + // Mark the task as complete + [task setTaskCompletedWithSuccess:YES]; +} + RCT_EXPORT_METHOD(defineTask:(NSString *)taskName taskExecutor:(RCTResponseSenderBlock)taskExecutor resolve:(RCTPromiseResolveBlock)resolve diff --git a/modules/background-task/src/index.ts b/modules/background-task/src/index.ts index bd764e6c779c..5001c8caa31f 100644 --- a/modules/background-task/src/index.ts +++ b/modules/background-task/src/index.ts @@ -16,7 +16,6 @@ const TaskManager = { throw new Error('Task executor must be a function'); } - console.log('native task manager', NativeReactNativeBackgroundTask); return NativeReactNativeBackgroundTask.defineTask(taskName, taskExecutor); }, }; diff --git a/src/setup/backgroundTask/index.native.ts b/src/setup/backgroundTask/index.native.ts index 9ac706ad3e22..5d452398a693 100644 --- a/src/setup/backgroundTask/index.native.ts +++ b/src/setup/backgroundTask/index.native.ts @@ -10,6 +10,7 @@ TaskManager.defineTask(BACKGROUND_FETCH_TASK, () => { }); function registerBackgroundFetch() { + console.log('probably not needed'); // return BackgroundFetch.registerTaskAsync(BACKGROUND_FETCH_TASK, { // minimumInterval: 60 * 15, // 15 minutes // stopOnTerminate: false, From f1819551abf2a91553e0aca81e792c5c4b56f198 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 26 Nov 2024 17:37:22 +0100 Subject: [PATCH 04/33] fix: remove useless condition --- .../background-task/ios/ReactNativeBackgroundTask.mm | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index 601cfa268ae4..f09fb5b452e8 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -31,13 +31,11 @@ - (void)dealloc { - (void)handleAppDidFinishLaunching:(NSNotification *)notification { NSLog(@"[ReactNativeBackgroundTask] Registering background task handler"); - if (@available(iOS 13.0, *)) { - [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:@"com.szymonrybczak.chat" - usingQueue:dispatch_get_main_queue() - launchHandler:^(__kindof BGTask * _Nonnull task) { - [self handleBackgroundTask:task]; - }]; - } + [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:@"com.szymonrybczak.chat" + usingQueue:dispatch_get_main_queue() + launchHandler:^(__kindof BGTask * _Nonnull task) { + [self handleBackgroundTask:task]; + }]; } - (void)handleBackgroundTask:(BGTask *)task API_AVAILABLE(ios(13.0)) { From cc214f82f87fc706951a9c53c022b005e48cd3bb Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 27 Nov 2024 11:53:06 +0100 Subject: [PATCH 05/33] feat: properly register & add basic handler --- ios/NewExpensify.xcodeproj/project.pbxproj | 4 ++ ios/NewExpensify/AppDelegate.mm | 14 ++++- ios/NewExpensify/Info.plist | 4 ++ .../ios/RNBackgroundTaskManager.h | 22 ++++++++ .../ios/RNBackgroundTaskManager.m | 40 +++++++++++++++ .../ios/ReactNativeBackgroundTask.mm | 51 ++++++++++++++----- 6 files changed, 122 insertions(+), 13 deletions(-) create mode 100644 modules/background-task/ios/RNBackgroundTaskManager.h create mode 100644 modules/background-task/ios/RNBackgroundTaskManager.m diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 1131589c72ec..c8d825589bfb 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -40,6 +40,7 @@ 7FD73CA22B23CE9500420AF3 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 7FD73C9B2B23CE9500420AF3 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 8744C5400E24E379441C04A4 /* libPods-NewExpensify.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 59A21B2405370FDDD847C813 /* libPods-NewExpensify.a */; }; 9E17CB36A6B22BDD4BE53561 /* libPods-NotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9196A72C11B91A52A43D6E8A /* libPods-NotificationServiceExtension.a */; }; + AC131FBB2CF634F20010CE80 /* BackgroundTasks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AC131FBA2CF634F20010CE80 /* BackgroundTasks.framework */; }; ACA597C323AA39404655647F /* libPods-NewExpensify-NewExpensifyTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EF33B19FC6A7FE676839430D /* libPods-NewExpensify-NewExpensifyTests.a */; }; BDB853621F354EBB84E619C2 /* ExpensifyNewKansas-MediumItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = D2AFB39EC1D44BF9B91D3227 /* ExpensifyNewKansas-MediumItalic.otf */; }; D27CE6B77196EF3EF450EEAC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0D3F9E814828D91464DF9D35 /* PrivacyInfo.xcprivacy */; }; @@ -141,6 +142,7 @@ 8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-BoldItalic.otf"; path = "../assets/fonts/native/ExpensifyNeue-BoldItalic.otf"; sourceTree = ""; }; 8EFE0319D586C1078DB926FD /* Pods-NewExpensify.releaseadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.releaseadhoc.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.releaseadhoc.xcconfig"; sourceTree = ""; }; 9196A72C11B91A52A43D6E8A /* libPods-NotificationServiceExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NotificationServiceExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + AC131FBA2CF634F20010CE80 /* BackgroundTasks.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BackgroundTasks.framework; path = System/Library/Frameworks/BackgroundTasks.framework; sourceTree = SDKROOT; }; BBE493797E97F2995E627244 /* Pods-NotificationServiceExtension.debugadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.debugadhoc.xcconfig"; path = "Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debugadhoc.xcconfig"; sourceTree = ""; }; BCD444BEDDB0AF1745B39049 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-NewExpensify-NewExpensifyTests/ExpoModulesProvider.swift"; sourceTree = ""; }; BF6A4C5167244B9FB8E4D4E3 /* ExpensifyNeue-Italic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-Italic.otf"; path = "../assets/fonts/native/ExpensifyNeue-Italic.otf"; sourceTree = ""; }; @@ -180,6 +182,7 @@ 383643682B6D4AE2005BB9AE /* DeviceCheck.framework in Frameworks */, E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, + AC131FBB2CF634F20010CE80 /* BackgroundTasks.framework in Frameworks */, 8744C5400E24E379441C04A4 /* libPods-NewExpensify.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -239,6 +242,7 @@ 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { isa = PBXGroup; children = ( + AC131FBA2CF634F20010CE80 /* BackgroundTasks.framework */, 383643672B6D4AE2005BB9AE /* DeviceCheck.framework */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED2971642150620600B7C4FE /* JavaScriptCore.framework */, diff --git a/ios/NewExpensify/AppDelegate.mm b/ios/NewExpensify/AppDelegate.mm index 5608c44823f4..84f785790c73 100644 --- a/ios/NewExpensify/AppDelegate.mm +++ b/ios/NewExpensify/AppDelegate.mm @@ -9,6 +9,8 @@ #import "RCTBootSplash.h" #import "RCTStartupTimer.h" #import +#import +#import @interface AppDelegate () @@ -49,7 +51,17 @@ - (BOOL)application:(UIApplication *)application [UIApplication sharedApplication].applicationIconBadgeNumber = 0; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isFirstRunComplete"]; } - + + [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:@"com.szymonrybczak.chat" + usingQueue:nil + launchHandler:^(BGTask * _Nonnull task) { + NSLog(@"[ReactNativeBackgroundTask] Executing background task: %@", task.identifier); + void (^handler)(BGTask * _Nonnull) = [[RNBackgroundTaskManager shared] handlerForIdentifier:task.identifier]; + if (handler) { + handler(task); + } + }]; + return YES; } diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 318fc3759167..747e32e65464 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -100,6 +100,10 @@ fetch processing + BGTaskSchedulerPermittedIdentifiers + + com.szymonrybczak.chat + UIFileSharingEnabled UILaunchStoryboardName diff --git a/modules/background-task/ios/RNBackgroundTaskManager.h b/modules/background-task/ios/RNBackgroundTaskManager.h new file mode 100644 index 000000000000..18c7c3f0c6e9 --- /dev/null +++ b/modules/background-task/ios/RNBackgroundTaskManager.h @@ -0,0 +1,22 @@ +// +// RNBackgroundTaskManager.h +// Pods +// +// Created by Szymon Rybczak on 27/11/2024. +// + + +#import +#import +#import + +@interface RNBackgroundTaskManager : NSObject + +@property (nonatomic, copy) void (^taskHandler)(BGTask * _Nonnull); + ++ (instancetype)shared; +- (void)setHandlerForIdentifier:(NSString *)identifier + completion:(void (^)(BGTask * _Nonnull))handler; +- (void (^)(BGTask * _Nonnull))handlerForIdentifier:(NSString *)identifier; + +@end diff --git a/modules/background-task/ios/RNBackgroundTaskManager.m b/modules/background-task/ios/RNBackgroundTaskManager.m new file mode 100644 index 000000000000..585012ed3c93 --- /dev/null +++ b/modules/background-task/ios/RNBackgroundTaskManager.m @@ -0,0 +1,40 @@ +// +// RNBackgroundTaskManager.m +// Pods +// +// Created by Szymon Rybczak on 27/11/2024. +// + +#import + +// RNBackgroundTaskManager.m +@implementation RNBackgroundTaskManager : NSObject { + NSMutableDictionary *_handlers; +} + ++ (instancetype)shared { + static RNBackgroundTaskManager *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[RNBackgroundTaskManager alloc] init]; + }); + return instance; +} + +- (instancetype)init { + if (self = [super init]) { + _handlers = [NSMutableDictionary new]; + } + return self; +} + +- (void)setHandlerForIdentifier:(NSString *)identifier + completion:(void (^)(BGTask * _Nonnull))handler { + _handlers[identifier] = handler; +} + +- (void (^)(BGTask * _Nonnull))handlerForIdentifier:(NSString *)identifier { + return _handlers[identifier]; +} + +@end diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index f09fb5b452e8..4e4c1eaa9b84 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -1,6 +1,7 @@ #import "ReactNativeBackgroundTask.h" #import #import +#import "RNBackgroundTaskManager.h" @implementation ReactNativeBackgroundTask { NSMutableDictionary *_taskExecutors; @@ -27,19 +28,25 @@ - (instancetype)init { - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - -- (void)handleAppDidFinishLaunching:(NSNotification *)notification { - NSLog(@"[ReactNativeBackgroundTask] Registering background task handler"); - - [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:@"com.szymonrybczak.chat" - usingQueue:dispatch_get_main_queue() - launchHandler:^(__kindof BGTask * _Nonnull task) { - [self handleBackgroundTask:task]; - }]; -} +// +//- (void)handleAppDidFinishLaunching:(NSNotification *)notification { +// NSLog(@"[ReactNativeBackgroundTask] handleAppDidFinishLaunching"); +// +// if (@available(iOS 13.0, *)) { +// // Ensure we're on the main thread +// if ([NSThread isMainThread]) { +// [self registerBackgroundTask]; +// } else { +// dispatch_async(dispatch_get_main_queue(), ^{ +// [self registerBackgroundTask]; +// }); +// } +// } +//} - (void)handleBackgroundTask:(BGTask *)task API_AVAILABLE(ios(13.0)) { // Create a task request to schedule the next background task +// BGProcessingTaskRequest TODO: use it BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:@"com.szymonrybczak.chat"]; request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15 * 60]; // Schedule for 15 minutes from now @@ -78,10 +85,30 @@ - (void)handleBackgroundTask:(BGTask *)task API_AVAILABLE(ios(13.0)) { reject(@"ERR_INVALID_TASK_EXECUTOR", @"Task executor must be provided", nil); return; } - + NSLog(@"[ReactNativeBackgroundTask] Defining task: %@", taskName); + + [[RNBackgroundTaskManager shared] setHandlerForIdentifier:@"com.szymonrybczak.chat" completion:^(BGTask * _Nonnull task) { + // Your background task handling code + NSLog(@"[ReactNativeBackgroundTask] Executing background task's handler"); + [task setTaskCompletedWithSuccess:YES]; + }]; + + + BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:@"com.szymonrybczak.chat"]; + // Set earliest begin date to some time in the future + request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15 * 60]; // 15 minutes from now + + NSError *error = nil; + if ([[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error]) { + resolve(@YES); + } else { + reject(@"error", error.localizedDescription, error); + } + + _taskExecutors[taskName] = taskExecutor; - resolve(nil); +// resolve(nil); } // Don't compile this code when we build for the old architecture. From 4a55c9bf668d36bc06281031d8e12e265d72e2be Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 27 Nov 2024 12:36:41 +0100 Subject: [PATCH 06/33] feat: execute js function from native --- .../background-task/ios/ReactNativeBackgroundTask.mm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index 4e4c1eaa9b84..7eb31549e8d3 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -89,8 +89,18 @@ - (void)handleBackgroundTask:(BGTask *)task API_AVAILABLE(ios(13.0)) { NSLog(@"[ReactNativeBackgroundTask] Defining task: %@", taskName); [[RNBackgroundTaskManager shared] setHandlerForIdentifier:@"com.szymonrybczak.chat" completion:^(BGTask * _Nonnull task) { - // Your background task handling code NSLog(@"[ReactNativeBackgroundTask] Executing background task's handler"); + + // Execute all registered tasks + [_taskExecutors enumerateKeysAndObjectsUsingBlock:^(NSString *taskName, RCTResponseSenderBlock executor, BOOL *stop) { + NSLog(@"[ReactNativeBackgroundTask] Executing task: %@", taskName); + executor(@[@{ + @"taskName": taskName, + @"type": @"background", + @"identifier": task.identifier + }]); + }]; + [task setTaskCompletedWithSuccess:YES]; }]; From ec9680c7409e268d3a66ced5d045ce6ce2b7c45c Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 27 Nov 2024 12:37:11 +0100 Subject: [PATCH 07/33] chore: clean up usage on JS side --- src/App.tsx | 7 +------ src/setup/backgroundTask/index.native.ts | 13 ------------- src/setup/backgroundTask/index.ts | 3 --- 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index ad04b45a1dfa..f95bc7297938 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -40,7 +40,7 @@ import {ReportIDsContextProvider} from './hooks/useReportIDs'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext'; import type {Route} from './ROUTES'; -import registerBackgroundFetch from './setup/backgroundTask'; +import './setup/backgroundTask'; import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; type AppProps = { @@ -65,11 +65,6 @@ function App({url}: AppProps) { useDefaultDragAndDrop(); OnyxUpdateManager(); - // TODO: move to correct place - useEffect(() => { - registerBackgroundFetch(); - }, []); - return ( diff --git a/src/setup/backgroundTask/index.native.ts b/src/setup/backgroundTask/index.native.ts index 5d452398a693..f73d5f9a11cd 100644 --- a/src/setup/backgroundTask/index.native.ts +++ b/src/setup/backgroundTask/index.native.ts @@ -1,5 +1,3 @@ -// import * as BackgroundFetch from 'expo-background-fetch'; -// import {defineTask} from 'expo-task-manager'; import TaskManager from '@expensify/react-native-background-task'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; @@ -8,14 +6,3 @@ const BACKGROUND_FETCH_TASK = 'FLUSH-SEQUENTIAL-QUEUE-BACKGROUND-FETCH'; TaskManager.defineTask(BACKGROUND_FETCH_TASK, () => { SequentialQueue.flush(); }); - -function registerBackgroundFetch() { - console.log('probably not needed'); - // return BackgroundFetch.registerTaskAsync(BACKGROUND_FETCH_TASK, { - // minimumInterval: 60 * 15, // 15 minutes - // stopOnTerminate: false, - // startOnBoot: true, - // }); -} - -export default registerBackgroundFetch; diff --git a/src/setup/backgroundTask/index.ts b/src/setup/backgroundTask/index.ts index 9c1b88dfd8e7..e69de29bb2d1 100644 --- a/src/setup/backgroundTask/index.ts +++ b/src/setup/backgroundTask/index.ts @@ -1,3 +0,0 @@ -function registerBackgroundFetch() {} - -export default registerBackgroundFetch; From 8da06e22a8c9f7fb3c338b27d0d8bcb6751d5fbb Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 27 Nov 2024 14:17:32 +0100 Subject: [PATCH 08/33] fix: replace `BGProcessingTaskRequest` with `BGProcessingTaskRequest` --- modules/background-task/ios/ReactNativeBackgroundTask.mm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index 7eb31549e8d3..665728db3c13 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -46,8 +46,7 @@ - (void)dealloc { - (void)handleBackgroundTask:(BGTask *)task API_AVAILABLE(ios(13.0)) { // Create a task request to schedule the next background task -// BGProcessingTaskRequest TODO: use it - BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:@"com.szymonrybczak.chat"]; + BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:@"com.szymonrybczak.chat"]; request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15 * 60]; // Schedule for 15 minutes from now NSError *error = nil; @@ -105,7 +104,7 @@ - (void)handleBackgroundTask:(BGTask *)task API_AVAILABLE(ios(13.0)) { }]; - BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:@"com.szymonrybczak.chat"]; + BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:@"com.szymonrybczak.chat"]; // Set earliest begin date to some time in the future request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15 * 60]; // 15 minutes from now From 6a06a97fd9270b96109d6b297643d1c1a69d302f Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 3 Dec 2024 17:45:44 +0100 Subject: [PATCH 09/33] chore: small code cleaning --- ios/Podfile | 1 + src/App.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ios/Podfile b/ios/Podfile index e33b15a0ba3c..b74584b839b4 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -89,6 +89,7 @@ target 'NewExpensify' do :app_path => "#{Pod::Config.instance.installation_root}/.." ) + target 'NewExpensifyTests' do inherit! :complete # Pods for testing diff --git a/src/App.tsx b/src/App.tsx index f95bc7297938..420901e4999e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import {PortalProvider} from '@gorhom/portal'; -import React, {useEffect} from 'react'; +import React from 'react'; import {LogBox} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import {PickerStateProvider} from 'react-native-picker-select'; From aefe68e0238e503e5eab91c01bc89c8206693fb5 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 4 Dec 2024 08:54:03 +0100 Subject: [PATCH 10/33] fix: remove android related stuff for now --- modules/background-task/android/build.gradle | 126 ------------------ .../background-task/android/gradle.properties | 5 - .../android/src/main/AndroidManifest.xml | 3 - .../android/src/main/AndroidManifestNew.xml | 2 - .../ReactNativeBackgroundTaskModule.kt | 24 ---- .../ReactNativeBackgroundTaskPackage.kt | 35 ----- .../newarch/ReactNativeBackgroundTaskSpec.kt | 7 - .../oldarch/ReactNativeBackgroundTaskSpec.kt | 11 -- .../background-task/react-native.config.js | 10 +- .../{index.native.ts => index.ios.ts} | 0 10 files changed, 4 insertions(+), 219 deletions(-) delete mode 100644 modules/background-task/android/build.gradle delete mode 100644 modules/background-task/android/gradle.properties delete mode 100644 modules/background-task/android/src/main/AndroidManifest.xml delete mode 100644 modules/background-task/android/src/main/AndroidManifestNew.xml delete mode 100644 modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt delete mode 100644 modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt delete mode 100644 modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt delete mode 100644 modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt rename src/setup/backgroundTask/{index.native.ts => index.ios.ts} (100%) diff --git a/modules/background-task/android/build.gradle b/modules/background-task/android/build.gradle deleted file mode 100644 index 335b1fa6bde2..000000000000 --- a/modules/background-task/android/build.gradle +++ /dev/null @@ -1,126 +0,0 @@ -buildscript { - // Buildscript is evaluated before everything else so we can't use getExtOrDefault - def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["ReactNativeBackgroundTask_kotlinVersion"] - - repositories { - google() - mavenCentral() - } - - dependencies { - classpath "com.android.tools.build:gradle:7.2.1" - // noinspection DifferentKotlinGradleVersion - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -def reactNativeArchitectures() { - def value = rootProject.getProperties().get("reactNativeArchitectures") - return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] -} - -def isNewArchitectureEnabled() { - return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" -} - -apply plugin: "com.android.library" -apply plugin: "kotlin-android" - -if (isNewArchitectureEnabled()) { - apply plugin: "com.facebook.react" -} - -def getExtOrDefault(name) { - return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["ReactNativeBackgroundTask_" + name] -} - -def getExtOrIntegerDefault(name) { - return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["ReactNativeBackgroundTask_" + name]).toInteger() -} - -def supportsNamespace() { - def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.') - def major = parsed[0].toInteger() - def minor = parsed[1].toInteger() - - // Namespace support was added in 7.3.0 - return (major == 7 && minor >= 3) || major >= 8 -} - -android { - if (supportsNamespace()) { - namespace "com.expensify.reactnativebackgroundtask" - - sourceSets { - main { - manifest.srcFile "src/main/AndroidManifestNew.xml" - } - } - } - - compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") - - defaultConfig { - minSdkVersion getExtOrIntegerDefault("minSdkVersion") - targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") - buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() - - } - - buildFeatures { - buildConfig true - } - - buildTypes { - release { - minifyEnabled false - } - } - - lintOptions { - disable "GradleCompatible" - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - sourceSets { - main { - if (isNewArchitectureEnabled()) { - java.srcDirs += [ - "src/newarch", - // Codegen specs - "generated/java", - "generated/jni" - ] - } else { - java.srcDirs += ["src/oldarch"] - } - } - } -} - -repositories { - mavenCentral() - google() -} - -def kotlin_version = getExtOrDefault("kotlinVersion") - -dependencies { - // For < 0.71, this will be from the local maven repo - // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin - //noinspection GradleDynamicVersion - implementation "com.facebook.react:react-native:+" - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" -} - -if (isNewArchitectureEnabled()) { - react { - jsRootDir = file("../src/") - libraryName = "ReactNativeBackgroundTask" - codegenJavaPackageName = "com.expensify.reactnativebackgroundtask" - } -} diff --git a/modules/background-task/android/gradle.properties b/modules/background-task/android/gradle.properties deleted file mode 100644 index 6ed0dc89b17c..000000000000 --- a/modules/background-task/android/gradle.properties +++ /dev/null @@ -1,5 +0,0 @@ -ReactNativeBackgroundTask_kotlinVersion=1.7.0 -ReactNativeBackgroundTask_minSdkVersion=21 -ReactNativeBackgroundTask_targetSdkVersion=31 -ReactNativeBackgroundTask_compileSdkVersion=31 -ReactNativeBackgroundTask_ndkversion=21.4.7075529 diff --git a/modules/background-task/android/src/main/AndroidManifest.xml b/modules/background-task/android/src/main/AndroidManifest.xml deleted file mode 100644 index 327870652628..000000000000 --- a/modules/background-task/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/modules/background-task/android/src/main/AndroidManifestNew.xml b/modules/background-task/android/src/main/AndroidManifestNew.xml deleted file mode 100644 index a2f47b6057db..000000000000 --- a/modules/background-task/android/src/main/AndroidManifestNew.xml +++ /dev/null @@ -1,2 +0,0 @@ - - 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 deleted file mode 100644 index c985b33aa800..000000000000 --- a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskModule.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.expensify.reactnativebackgroundtask - -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.bridge.Promise - -class ReactNativeBackgroundTaskModule internal constructor(context: ReactApplicationContext) : - ReactNativeBackgroundTaskSpec(context) { - - override fun getName(): String { - return NAME - } - - // Example method - // See https://reactnative.dev/docs/native-modules-android - @ReactMethod - override fun multiply(a: Double, b: Double, promise: Promise) { - promise.resolve(a * b) - } - - companion object { - const val NAME = "ReactNativeBackgroundTask" - } -} diff --git a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt b/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt deleted file mode 100644 index dd0547f7ef35..000000000000 --- a/modules/background-task/android/src/main/java/com/expensify/reactnativebackgroundtask/ReactNativeBackgroundTaskPackage.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.expensify.reactnativebackgroundtask - -import com.facebook.react.TurboReactPackage -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.NativeModule -import com.facebook.react.module.model.ReactModuleInfoProvider -import com.facebook.react.module.model.ReactModuleInfo -import java.util.HashMap - -class ReactNativeBackgroundTaskPackage : TurboReactPackage() { - override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { - return if (name == ReactNativeBackgroundTaskModule.NAME) { - ReactNativeBackgroundTaskModule(reactContext) - } else { - null - } - } - - override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { - return ReactModuleInfoProvider { - val moduleInfos: MutableMap = HashMap() - val isTurboModule: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - moduleInfos[ReactNativeBackgroundTaskModule.NAME] = ReactModuleInfo( - ReactNativeBackgroundTaskModule.NAME, - ReactNativeBackgroundTaskModule.NAME, - false, // canOverrideExistingModule - false, // needsEagerInit - true, // hasConstants - false, // isCxxModule - isTurboModule // isTurboModule - ) - moduleInfos - } - } -} diff --git a/modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt b/modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt deleted file mode 100644 index 2ee39c49c66d..000000000000 --- a/modules/background-task/android/src/newarch/ReactNativeBackgroundTaskSpec.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.expensify.reactnativebackgroundtask - -import com.facebook.react.bridge.ReactApplicationContext - -abstract class ReactNativeBackgroundTaskSpec internal constructor(context: ReactApplicationContext) : - NativeReactNativeBackgroundTaskSpec(context) { -} diff --git a/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt b/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt deleted file mode 100644 index 771e6a13734b..000000000000 --- a/modules/background-task/android/src/oldarch/ReactNativeBackgroundTaskSpec.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.expensify.reactnativebackgroundtask - -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactContextBaseJavaModule -import com.facebook.react.bridge.Promise - -abstract class ReactNativeBackgroundTaskSpec internal constructor(context: ReactApplicationContext) : - ReactContextBaseJavaModule(context) { - - abstract fun multiply(a: Double, b: Double, promise: Promise) -} diff --git a/modules/background-task/react-native.config.js b/modules/background-task/react-native.config.js index 66211dab797e..d532440e69b0 100644 --- a/modules/background-task/react-native.config.js +++ b/modules/background-task/react-native.config.js @@ -2,11 +2,9 @@ * @type {import('@react-native-community/cli-types').UserDependencyConfig} */ module.exports = { - dependency: { - platforms: { - android: { - cmakeListsPath: 'build/generated/source/codegen/jni/CMakeLists.txt', - }, + dependency: { + platforms: { + android: null, + }, }, - }, }; diff --git a/src/setup/backgroundTask/index.native.ts b/src/setup/backgroundTask/index.ios.ts similarity index 100% rename from src/setup/backgroundTask/index.native.ts rename to src/setup/backgroundTask/index.ios.ts From 2f6dc68b7d0f8a62d5270577fe964655d8158cb8 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Mon, 9 Dec 2024 10:50:17 +0100 Subject: [PATCH 11/33] fix: replace hardcoded identifier --- ios/NewExpensify/AppDelegate.mm | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/ios/NewExpensify/AppDelegate.mm b/ios/NewExpensify/AppDelegate.mm index 84f785790c73..c379853dd805 100644 --- a/ios/NewExpensify/AppDelegate.mm +++ b/ios/NewExpensify/AppDelegate.mm @@ -52,16 +52,24 @@ - (BOOL)application:(UIApplication *)application [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isFirstRunComplete"]; } - [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:@"com.szymonrybczak.chat" - usingQueue:nil - launchHandler:^(BGTask * _Nonnull task) { - NSLog(@"[ReactNativeBackgroundTask] Executing background task: %@", task.identifier); - void (^handler)(BGTask * _Nonnull) = [[RNBackgroundTaskManager shared] handlerForIdentifier:task.identifier]; - if (handler) { - handler(task); + NSArray *backgroundIdentifiers = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"BGTaskSchedulerPermittedIdentifiers"]; + + if (!backgroundIdentifiers || ![backgroundIdentifiers isKindOfClass:[NSArray class]]) { + NSLog(@"[ReactNativeBackgroundTask] No background identifiers found or invalid format"); + } else { + for (NSString *identifier in backgroundIdentifiers) { + [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:identifier + usingQueue:nil + launchHandler:^(BGTask * _Nonnull task) { + NSLog(@"[ReactNativeBackgroundTask] Executing background task: %@", task.identifier); + void (^handler)(BGTask * _Nonnull) = [[RNBackgroundTaskManager shared] handlerForIdentifier:task.identifier]; + if (handler) { + handler(task); + } + }]; } - }]; - + } + return YES; } From d4d6a31c75a9c1384f1e55b5de1be4790fb39253 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Mon, 9 Dec 2024 11:19:29 +0100 Subject: [PATCH 12/33] chore: code cleanup --- .../ios/ReactNativeBackgroundTask.mm | 105 +++++++----------- 1 file changed, 38 insertions(+), 67 deletions(-) diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index 665728db3c13..d52d8a9ba3e5 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -28,45 +28,6 @@ - (instancetype)init { - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } -// -//- (void)handleAppDidFinishLaunching:(NSNotification *)notification { -// NSLog(@"[ReactNativeBackgroundTask] handleAppDidFinishLaunching"); -// -// if (@available(iOS 13.0, *)) { -// // Ensure we're on the main thread -// if ([NSThread isMainThread]) { -// [self registerBackgroundTask]; -// } else { -// dispatch_async(dispatch_get_main_queue(), ^{ -// [self registerBackgroundTask]; -// }); -// } -// } -//} - -- (void)handleBackgroundTask:(BGTask *)task API_AVAILABLE(ios(13.0)) { - // Create a task request to schedule the next background task - BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:@"com.szymonrybczak.chat"]; - request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15 * 60]; // Schedule for 15 minutes from now - - NSError *error = nil; - if (![[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error]) { - NSLog(@"[ReactNativeBackgroundTask] Failed to schedule next task: %@", error); - } - - // Execute all registered tasks - [_taskExecutors enumerateKeysAndObjectsUsingBlock:^(NSString *taskName, RCTResponseSenderBlock executor, BOOL *stop) { - NSLog(@"[ReactNativeBackgroundTask] Executing background task: %@", taskName); - executor(@[@{ - @"taskName": taskName, - @"type": @"background", - @"identifier": task.identifier - }]); - }]; - - // Mark the task as complete - [task setTaskCompletedWithSuccess:YES]; -} RCT_EXPORT_METHOD(defineTask:(NSString *)taskName taskExecutor:(RCTResponseSenderBlock)taskExecutor @@ -85,39 +46,49 @@ - (void)handleBackgroundTask:(BGTask *)task API_AVAILABLE(ios(13.0)) { return; } - NSLog(@"[ReactNativeBackgroundTask] Defining task: %@", taskName); + NSArray *backgroundIdentifiers = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"BGTaskSchedulerPermittedIdentifiers"]; - [[RNBackgroundTaskManager shared] setHandlerForIdentifier:@"com.szymonrybczak.chat" completion:^(BGTask * _Nonnull task) { - NSLog(@"[ReactNativeBackgroundTask] Executing background task's handler"); - - // Execute all registered tasks - [_taskExecutors enumerateKeysAndObjectsUsingBlock:^(NSString *taskName, RCTResponseSenderBlock executor, BOOL *stop) { - NSLog(@"[ReactNativeBackgroundTask] Executing task: %@", taskName); - executor(@[@{ - @"taskName": taskName, - @"type": @"background", - @"identifier": task.identifier - }]); - }]; - - [task setTaskCompletedWithSuccess:YES]; - }]; + if (!backgroundIdentifiers || ![backgroundIdentifiers isKindOfClass:[NSArray class]]) { + NSLog(@"[ReactNativeBackgroundTask] No background identifiers found or invalid format"); + reject(@"ERR_INVALID_TASK_SCHEDULER_IDENTIFIER", @"No background identifiers found or invalid format", nil); + return; + } + + NSLog(@"[ReactNativeBackgroundTask] Defining task: %@", taskName); - BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:@"com.szymonrybczak.chat"]; - // Set earliest begin date to some time in the future - request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15 * 60]; // 15 minutes from now + for (NSString *identifier in backgroundIdentifiers) { + [[RNBackgroundTaskManager shared] setHandlerForIdentifier:identifier completion:^(BGTask * _Nonnull task) { + NSLog(@"[ReactNativeBackgroundTask] Executing background task's handler"); - NSError *error = nil; - if ([[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error]) { - resolve(@YES); - } else { - reject(@"error", error.localizedDescription, error); - } - - + // Execute all registered tasks + [self->_taskExecutors enumerateKeysAndObjectsUsingBlock:^(NSString *taskName, RCTResponseSenderBlock executor, BOOL *stop) { + NSLog(@"[ReactNativeBackgroundTask] Executing task: %@", taskName); + executor(@[@{ + @"taskName": taskName, + @"type": @"background", + @"identifier": task.identifier + }]); + }]; + + [task setTaskCompletedWithSuccess:YES]; + }]; + + + BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:identifier]; + + // Set earliest begin date to some time in the future + request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15 * 60]; // 15 minutes from now + + NSError *error = nil; + if ([[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error]) { + resolve(@YES); + } else { + reject(@"error", error.localizedDescription, error); + } + } + _taskExecutors[taskName] = taskExecutor; -// resolve(nil); } // Don't compile this code when we build for the old architecture. From 5de130db96f401635a05f8d124efabf4784e943a Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Mon, 9 Dec 2024 12:09:45 +0100 Subject: [PATCH 13/33] chore: one line impl in `AppDelegate.mm` --- ios/NewExpensify/AppDelegate.mm | 18 +---------------- .../ios/RNBackgroundTaskManager.h | 3 ++- .../ios/RNBackgroundTaskManager.m | 20 +++++++++++++++++++ .../ios/ReactNativeBackgroundTask.h | 3 --- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/ios/NewExpensify/AppDelegate.mm b/ios/NewExpensify/AppDelegate.mm index c379853dd805..5d419f5a623e 100644 --- a/ios/NewExpensify/AppDelegate.mm +++ b/ios/NewExpensify/AppDelegate.mm @@ -52,23 +52,7 @@ - (BOOL)application:(UIApplication *)application [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isFirstRunComplete"]; } - NSArray *backgroundIdentifiers = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"BGTaskSchedulerPermittedIdentifiers"]; - - if (!backgroundIdentifiers || ![backgroundIdentifiers isKindOfClass:[NSArray class]]) { - NSLog(@"[ReactNativeBackgroundTask] No background identifiers found or invalid format"); - } else { - for (NSString *identifier in backgroundIdentifiers) { - [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:identifier - usingQueue:nil - launchHandler:^(BGTask * _Nonnull task) { - NSLog(@"[ReactNativeBackgroundTask] Executing background task: %@", task.identifier); - void (^handler)(BGTask * _Nonnull) = [[RNBackgroundTaskManager shared] handlerForIdentifier:task.identifier]; - if (handler) { - handler(task); - } - }]; - } - } + [RNBackgroundTaskManager setup]; return YES; } diff --git a/modules/background-task/ios/RNBackgroundTaskManager.h b/modules/background-task/ios/RNBackgroundTaskManager.h index 18c7c3f0c6e9..165150b4219c 100644 --- a/modules/background-task/ios/RNBackgroundTaskManager.h +++ b/modules/background-task/ios/RNBackgroundTaskManager.h @@ -15,7 +15,8 @@ @property (nonatomic, copy) void (^taskHandler)(BGTask * _Nonnull); + (instancetype)shared; -- (void)setHandlerForIdentifier:(NSString *)identifier ++ (void)setup; +- (void)setHandlerForIdentifier:(NSString *)identifier completion:(void (^)(BGTask * _Nonnull))handler; - (void (^)(BGTask * _Nonnull))handlerForIdentifier:(NSString *)identifier; diff --git a/modules/background-task/ios/RNBackgroundTaskManager.m b/modules/background-task/ios/RNBackgroundTaskManager.m index 585012ed3c93..f8e665630071 100644 --- a/modules/background-task/ios/RNBackgroundTaskManager.m +++ b/modules/background-task/ios/RNBackgroundTaskManager.m @@ -37,4 +37,24 @@ - (void)setHandlerForIdentifier:(NSString *)identifier return _handlers[identifier]; } ++ (void)setup { + NSArray *backgroundIdentifiers = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"BGTaskSchedulerPermittedIdentifiers"]; + + if (!backgroundIdentifiers || ![backgroundIdentifiers isKindOfClass:[NSArray class]]) { + NSLog(@"[ReactNativeBackgroundTask] No background identifiers found or invalid format"); + } else { + for (NSString *identifier in backgroundIdentifiers) { + [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:identifier + usingQueue:nil + launchHandler:^(BGTask * _Nonnull task) { + NSLog(@"[ReactNativeBackgroundTask] Executing background task: %@", task.identifier); + void (^handler)(BGTask * _Nonnull) = [[RNBackgroundTaskManager shared] handlerForIdentifier:task.identifier]; + if (handler) { + handler(task); + } + }]; + } + } +} + @end diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.h b/modules/background-task/ios/ReactNativeBackgroundTask.h index 34070299740a..8ddedaeb6fad 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.h +++ b/modules/background-task/ios/ReactNativeBackgroundTask.h @@ -15,7 +15,4 @@ resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject; -- (void)handleAppDidFinishLaunching:(NSNotification *)notification; -- (void)handleBackgroundTask:(BGTask *)task API_AVAILABLE(ios(13.0)); - @end From 138ad72978cd8c401ea5f6fc8c6ea82c1ab00918 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Mon, 9 Dec 2024 12:11:00 +0100 Subject: [PATCH 14/33] chore: remove header comments --- modules/background-task/ios/RNBackgroundTaskManager.h | 8 -------- modules/background-task/ios/RNBackgroundTaskManager.m | 8 -------- 2 files changed, 16 deletions(-) diff --git a/modules/background-task/ios/RNBackgroundTaskManager.h b/modules/background-task/ios/RNBackgroundTaskManager.h index 165150b4219c..9902149fcfbc 100644 --- a/modules/background-task/ios/RNBackgroundTaskManager.h +++ b/modules/background-task/ios/RNBackgroundTaskManager.h @@ -1,11 +1,3 @@ -// -// RNBackgroundTaskManager.h -// Pods -// -// Created by Szymon Rybczak on 27/11/2024. -// - - #import #import #import diff --git a/modules/background-task/ios/RNBackgroundTaskManager.m b/modules/background-task/ios/RNBackgroundTaskManager.m index f8e665630071..f0bc85dfaa9f 100644 --- a/modules/background-task/ios/RNBackgroundTaskManager.m +++ b/modules/background-task/ios/RNBackgroundTaskManager.m @@ -1,13 +1,5 @@ -// -// RNBackgroundTaskManager.m -// Pods -// -// Created by Szymon Rybczak on 27/11/2024. -// - #import -// RNBackgroundTaskManager.m @implementation RNBackgroundTaskManager : NSObject { NSMutableDictionary *_handlers; } From 5d2abd162d1b13e050fd2a48ec682db0df25fed3 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Mon, 9 Dec 2024 14:30:35 +0100 Subject: [PATCH 15/33] feat: implement sequential task scheduling --- .../ios/ReactNativeBackgroundTask.mm | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index d52d8a9ba3e5..44196d2f4bc2 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -29,6 +29,22 @@ - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (BOOL)scheduleNewBackgroundTask:(NSString *)identifier { + BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:identifier]; + + // Set earliest begin date to some time in the future + request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15 * 60]; // 15 minutes from now + + NSError *error = nil; + BOOL success = [[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error]; + + if (!success) { + NSLog(@"[ReactNativeBackgroundTask] Failed to schedule task: %@", error.localizedDescription); + } + + return success; +} + RCT_EXPORT_METHOD(defineTask:(NSString *)taskName taskExecutor:(RCTResponseSenderBlock)taskExecutor resolve:(RCTPromiseResolveBlock)resolve @@ -71,20 +87,18 @@ - (void)dealloc { }]); }]; + [self scheduleNewBackgroundTask:identifier]; + [task setTaskCompletedWithSuccess:YES]; }]; - - BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:identifier]; - - // Set earliest begin date to some time in the future - request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15 * 60]; // 15 minutes from now - - NSError *error = nil; - if ([[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error]) { + BOOL success = [self scheduleNewBackgroundTask:identifier]; + + if (success) { + _taskExecutors[taskName] = taskExecutor; resolve(@YES); } else { - reject(@"error", error.localizedDescription, error); + reject(@"error", @"Failed to schedule initial background task", nil); } } From 3a2b3c92d2f82c231a6a3678b3e0641fc755347e Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Mon, 9 Dec 2024 14:33:08 +0100 Subject: [PATCH 16/33] chore: remove useless observer --- .../ios/ReactNativeBackgroundTask.mm | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index 44196d2f4bc2..4ebfff9529d5 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -12,23 +12,10 @@ @implementation ReactNativeBackgroundTask { - (instancetype)init { if (self = [super init]) { _taskExecutors = [NSMutableDictionary new]; - - // Add observer in the next run loop to ensure proper registration - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleAppDidFinishLaunching:) - name:UIApplicationDidFinishLaunchingNotification - object:nil]; - }); } return self; } -// Add dealloc to properly remove the observer -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier { BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:identifier]; From 82c42ac8cb513d43effa57d8cbbeeb355ee0e14e Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 10 Dec 2024 09:49:36 +0100 Subject: [PATCH 17/33] fix: properly order packages in `package.json` --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 57940d641c06..4ac373b95aca 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "dependencies": { "@dotlottie/react-player": "^1.6.3", "@expensify/react-native-live-markdown": "0.1.210", + "@expensify/react-native-background-task": "file:./modules/background-task", "@expo/metro-runtime": "^4.0.0", "@firebase/app": "^0.10.10", "@firebase/performance": "^0.6.8", @@ -188,8 +189,7 @@ "react-plaid-link": "3.3.2", "react-web-config": "^1.0.0", "react-webcam": "^7.1.1", - "react-window": "^1.8.9", - "@expensify/react-native-background-task": "file:./modules/background-task" + "react-window": "^1.8.9" }, "devDependencies": { "@actions/core": "1.10.0", From 532a00747c64079296e833fa1c4c5c8993853927 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 10 Dec 2024 11:24:48 +0100 Subject: [PATCH 18/33] feat!: use new EventEmitter API --- ios/Podfile.lock | 25 +++++++++++++++++++ .../ios/ReactNativeBackgroundTask.h | 2 +- .../ios/ReactNativeBackgroundTask.mm | 6 +---- .../src/NativeReactNativeBackgroundTask.ts | 2 ++ modules/background-task/src/index.ts | 12 +++++++++ 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 58bae61afde8..4ddb40b55336 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -35,6 +35,27 @@ PODS: - EXImageLoader (5.0.0): - ExpoModulesCore - React-Core + - expensify-react-native-background-task (0.0.0): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - Expo (52.0.14): - ExpoModulesCore - ExpoAsset (11.0.1): @@ -2813,6 +2834,7 @@ DEPENDENCIES: - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - EXAV (from `../node_modules/expo-av/ios`) - EXImageLoader (from `../node_modules/expo-image-loader/ios`) + - "expensify-react-native-background-task (from `../node_modules/@expensify/react-native-background-task`)" - Expo (from `../node_modules/expo`) - ExpoAsset (from `../node_modules/expo-asset/ios`) - ExpoFont (from `../node_modules/expo-font/ios`) @@ -2982,6 +3004,8 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-av/ios" EXImageLoader: :path: "../node_modules/expo-image-loader/ios" + expensify-react-native-background-task: + :path: "../node_modules/@expensify/react-native-background-task" Expo: :path: "../node_modules/expo" ExpoAsset: @@ -3228,6 +3252,7 @@ SPEC CHECKSUMS: DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 EXAV: 9773c9799767c9925547b05e41a26a0240bb8ef2 EXImageLoader: 759063a65ab016b836f73972d3bb25404888713d + expensify-react-native-background-task: 6f797cf470b627912c246514b1631a205794775d Expo: 0e7b52be71a24a38d5e919e3040d8f51a8739cd0 ExpoAsset: 8138f2a9ec55ae1ad7c3871448379f7d97692d15 ExpoFont: 7522d869d84ee2ee8093ee997fef5b86f85d856b diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.h b/modules/background-task/ios/ReactNativeBackgroundTask.h index 8ddedaeb6fad..93d1e83a6878 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.h +++ b/modules/background-task/ios/ReactNativeBackgroundTask.h @@ -2,7 +2,7 @@ #import "RNReactNativeBackgroundTaskSpec.h" #import -@interface ReactNativeBackgroundTask : NSObject +@interface ReactNativeBackgroundTask : NativeReactNativeBackgroundTaskSpecBase #else #import #import diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index 4ebfff9529d5..c23649fd777a 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -67,11 +67,7 @@ - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier { // Execute all registered tasks [self->_taskExecutors enumerateKeysAndObjectsUsingBlock:^(NSString *taskName, RCTResponseSenderBlock executor, BOOL *stop) { NSLog(@"[ReactNativeBackgroundTask] Executing task: %@", taskName); - executor(@[@{ - @"taskName": taskName, - @"type": @"background", - @"identifier": task.identifier - }]); + [self emitOnBackgroundTaskExecution:(taskName)]; }]; [self scheduleNewBackgroundTask:identifier]; diff --git a/modules/background-task/src/NativeReactNativeBackgroundTask.ts b/modules/background-task/src/NativeReactNativeBackgroundTask.ts index ed2a381a4862..b71c4bef4bc9 100644 --- a/modules/background-task/src/NativeReactNativeBackgroundTask.ts +++ b/modules/background-task/src/NativeReactNativeBackgroundTask.ts @@ -1,8 +1,10 @@ import type {TurboModule} from 'react-native'; import {TurboModuleRegistry} from 'react-native'; +import type {EventEmitter} from 'react-native/Libraries/Types/CodegenTypes'; 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 5001c8caa31f..d3afe75a85bc 100644 --- a/modules/background-task/src/index.ts +++ b/modules/background-task/src/index.ts @@ -2,6 +2,16 @@ import NativeReactNativeBackgroundTask from './NativeReactNativeBackgroundTask'; type TaskManagerTaskExecutor = (data: T) => void | Promise; +const taskExecutors = new Map(); + +NativeReactNativeBackgroundTask.onBackgroundTaskExecution((taskName) => { + const executor = taskExecutors.get(taskName); + + if (executor) { + executor(taskName); + } +}); + const TaskManager = { /** * Defines a task that can be executed in the background. @@ -16,6 +26,8 @@ const TaskManager = { throw new Error('Task executor must be a function'); } + taskExecutors.set(taskName, taskExecutor); + return NativeReactNativeBackgroundTask.defineTask(taskName, taskExecutor); }, }; From 99bf5576edcc24b57c99a6b18cc1f7543c1a4da4 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 10 Dec 2024 12:16:12 +0100 Subject: [PATCH 19/33] fix: make 0 warnings --- modules/background-task/ios/RNBackgroundTaskManager.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/background-task/ios/RNBackgroundTaskManager.h b/modules/background-task/ios/RNBackgroundTaskManager.h index 9902149fcfbc..38a8a88e734d 100644 --- a/modules/background-task/ios/RNBackgroundTaskManager.h +++ b/modules/background-task/ios/RNBackgroundTaskManager.h @@ -4,12 +4,12 @@ @interface RNBackgroundTaskManager : NSObject -@property (nonatomic, copy) void (^taskHandler)(BGTask * _Nonnull); +@property (nonatomic, copy) void (^ _Nullable taskHandler)(BGTask * _Nonnull); -+ (instancetype)shared; ++ (instancetype _Nullable )shared; + (void)setup; -- (void)setHandlerForIdentifier:(NSString *)identifier - completion:(void (^)(BGTask * _Nonnull))handler; -- (void (^)(BGTask * _Nonnull))handlerForIdentifier:(NSString *)identifier; +- (void)setHandlerForIdentifier:(NSString *_Nullable)identifier + completion:(void (^_Nullable)(BGTask * _Nonnull))handler; +- (void (^_Nullable)(BGTask * _Nonnull))handlerForIdentifier:(NSString *_Nullable)identifier; @end From 94769ea812eee86856332a5f2c5109b683af43ef Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 10 Dec 2024 12:21:53 +0100 Subject: [PATCH 20/33] fix: add proper bundle id --- ios/NewExpensify/Info.plist | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 747e32e65464..68b404704f13 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -100,10 +100,10 @@ fetch processing - BGTaskSchedulerPermittedIdentifiers - - com.szymonrybczak.chat - + BGTaskSchedulerPermittedIdentifiers + + com.expensify.chat.dev + UIFileSharingEnabled UILaunchStoryboardName From a9d575503cc84e3204a73bb1e7cd24a5829eff31 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 10 Dec 2024 16:46:54 +0100 Subject: [PATCH 21/33] fix: add release bundle id --- ios/NewExpensify/Info.plist | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 68b404704f13..4eb117167f85 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -102,6 +102,7 @@ BGTaskSchedulerPermittedIdentifiers + com.expensify.chat.chat com.expensify.chat.dev UIFileSharingEnabled From e663e606761ccdaf446c2dc136895176bd9b599f Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 11 Dec 2024 10:15:23 +0100 Subject: [PATCH 22/33] fix: include all bundle identifiers --- ios/NewExpensify/Info.plist | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 4eb117167f85..98df368c2e41 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -2,6 +2,12 @@ + BGTaskSchedulerPermittedIdentifiers + + com.chat.expensify.chat + com.expensify.chat.dev + com.expensify.chat.adhoc + CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion @@ -100,11 +106,6 @@ fetch processing - BGTaskSchedulerPermittedIdentifiers - - com.expensify.chat.chat - com.expensify.chat.dev - UIFileSharingEnabled UILaunchStoryboardName From 1b252dd6c585323d3254d3833ce8e7ed50466661 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 17 Dec 2024 23:30:47 +0100 Subject: [PATCH 23/33] fix: replace `BGProcessingTaskRequest` with `BGAppRefreshTaskRequest` --- modules/background-task/ios/ReactNativeBackgroundTask.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index c23649fd777a..adc227415cff 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -17,7 +17,7 @@ - (instancetype)init { } - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier { - BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:identifier]; + BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:identifier]; // Set earliest begin date to some time in the future request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15 * 60]; // 15 minutes from now From 34813d65772e69341dba3f9790bc25a5b52516e7 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 17 Dec 2024 23:33:45 +0100 Subject: [PATCH 24/33] fix: call `resolve()` only once when there's multiple identifiers --- .../ios/ReactNativeBackgroundTask.mm | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index adc227415cff..8ac39f3707e5 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -60,6 +60,8 @@ - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier { NSLog(@"[ReactNativeBackgroundTask] Defining task: %@", taskName); + BOOL allSuccess = YES; + for (NSString *identifier in backgroundIdentifiers) { [[RNBackgroundTaskManager shared] setHandlerForIdentifier:identifier completion:^(BGTask * _Nonnull task) { NSLog(@"[ReactNativeBackgroundTask] Executing background task's handler"); @@ -79,11 +81,18 @@ - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier { if (success) { _taskExecutors[taskName] = taskExecutor; - resolve(@YES); } else { - reject(@"error", @"Failed to schedule initial background task", nil); + allSuccess = NO; + break; } } + + if (allSuccess) { + resolve(@YES); + } else { + reject(@"error", @"Failed to schedule initial background task", nil); + } + _taskExecutors[taskName] = taskExecutor; } From 0e504daa31de2617b8899cbb1277ed965b434c55 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Fri, 20 Dec 2024 17:37:31 +0100 Subject: [PATCH 25/33] fix: use bg task specific id --- ios/NewExpensify/Info.plist | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 98df368c2e41..e2308df8eb6d 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -4,9 +4,7 @@ BGTaskSchedulerPermittedIdentifiers - com.chat.expensify.chat - com.expensify.chat.dev - com.expensify.chat.adhoc + com.chat.expensify.backgroundTaskSync CADisableMinimumFrameDurationOnPhone From a062fc93f786a9a096d7ae29c2b011dbfb5e1057 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Mon, 30 Dec 2024 12:20:17 +0100 Subject: [PATCH 26/33] fix: remove oudated condition in `Podfile` --- ...nsify-react-native-background-task.podspec | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/modules/background-task/expensify-react-native-background-task.podspec b/modules/background-task/expensify-react-native-background-task.podspec index d66739edb3e5..207cd239c463 100644 --- a/modules/background-task/expensify-react-native-background-task.podspec +++ b/modules/background-task/expensify-react-native-background-task.podspec @@ -16,26 +16,5 @@ Pod::Spec.new do |s| s.source_files = "ios/**/*.{h,m,mm,cpp}" - # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. - # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. - if respond_to?(:install_modules_dependencies, true) - install_modules_dependencies(s) - else - s.dependency "React-Core" - - # Don't install the dependencies when we run `pod install` in the old architecture. - if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then - s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" - s.pod_target_xcconfig = { - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", - "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", - "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" - } - s.dependency "React-Codegen" - s.dependency "RCT-Folly" - s.dependency "RCTRequired" - s.dependency "RCTTypeSafety" - s.dependency "ReactCommon/turbomodule/core" - end - end + install_modules_dependencies(s) end From 49632c5e2939825227d9a91032a42b939e1a6233 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 7 Jan 2025 23:33:39 +0100 Subject: [PATCH 27/33] chore: update lock --- package-lock.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/package-lock.json b/package-lock.json index d19fa45d10d9..d058b70a832c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "license": "MIT", "dependencies": { "@dotlottie/react-player": "^1.6.3", + "@expensify/react-native-background-task": "file:./modules/background-task", "@expensify/react-native-live-markdown": "0.1.210", "@expo/metro-runtime": "^4.0.0", "@firebase/app": "^0.10.10", @@ -285,6 +286,11 @@ "npm": "10.8.2" } }, + "modules/background-task": { + "name": "@expensify/react-native-background-task", + "version": "0.0.0", + "license": "UNLICENSED" + }, "node_modules/@0no-co/graphql.web": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.11.tgz", @@ -3630,6 +3636,10 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@expensify/react-native-background-task": { + "resolved": "modules/background-task", + "link": true + }, "node_modules/@expensify/react-native-live-markdown": { "version": "0.1.210", "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.210.tgz", From a8884a0cad2b0f25b11c5e892a53b7a52ac0ffe3 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 7 Jan 2025 23:41:48 +0100 Subject: [PATCH 28/33] fix: eslint warnings --- modules/background-task/src/NativeReactNativeBackgroundTask.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/background-task/src/NativeReactNativeBackgroundTask.ts b/modules/background-task/src/NativeReactNativeBackgroundTask.ts index b71c4bef4bc9..792fe2850552 100644 --- a/modules/background-task/src/NativeReactNativeBackgroundTask.ts +++ b/modules/background-task/src/NativeReactNativeBackgroundTask.ts @@ -2,6 +2,8 @@ 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; From 74d6767106392522a0a44775cd53fd35afb77df7 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 8 Jan 2025 09:32:02 +0100 Subject: [PATCH 29/33] fix: mock module --- tests/ui/GroupChatNameTests.tsx | 5 +++++ tests/ui/PaginationTest.tsx | 5 +++++ tests/ui/SwitchToExpensifyClassicTest.tsx | 5 +++++ tests/ui/UnreadIndicatorsTest.tsx | 5 +++++ tests/ui/WorkspaceSwitcherTest.tsx | 5 +++++ tests/unit/loginTest.tsx | 5 +++++ 6 files changed, 30 insertions(+) diff --git a/tests/ui/GroupChatNameTests.tsx b/tests/ui/GroupChatNameTests.tsx index fc383efe4e28..eef08d7cbc40 100644 --- a/tests/ui/GroupChatNameTests.tsx +++ b/tests/ui/GroupChatNameTests.tsx @@ -22,6 +22,11 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(50000); +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + jest.mock('../../src/components/ConfirmedRoute.tsx'); // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest diff --git a/tests/ui/PaginationTest.tsx b/tests/ui/PaginationTest.tsx index 8fcd6dbac1d6..59e164180075 100644 --- a/tests/ui/PaginationTest.tsx +++ b/tests/ui/PaginationTest.tsx @@ -21,6 +21,11 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + jest.mock('@react-navigation/native'); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); diff --git a/tests/ui/SwitchToExpensifyClassicTest.tsx b/tests/ui/SwitchToExpensifyClassicTest.tsx index c8fe13b7e2e9..35c5ec1e58bb 100644 --- a/tests/ui/SwitchToExpensifyClassicTest.tsx +++ b/tests/ui/SwitchToExpensifyClassicTest.tsx @@ -16,6 +16,11 @@ const USER_A_EMAIL = 'user_a@test.com'; jest.setTimeout(60000); +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + jest.mock('@react-navigation/native'); TestHelper.setupApp(); diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index 6f4313a9f02c..9509ee81db0d 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -32,6 +32,11 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + jest.mock('@react-navigation/native'); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); diff --git a/tests/ui/WorkspaceSwitcherTest.tsx b/tests/ui/WorkspaceSwitcherTest.tsx index 614ed4e5ab70..3ae442823726 100644 --- a/tests/ui/WorkspaceSwitcherTest.tsx +++ b/tests/ui/WorkspaceSwitcherTest.tsx @@ -18,6 +18,11 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + jest.mock('@react-navigation/native', () => { const actualNav = jest.requireActual('@react-navigation/native'); return { diff --git a/tests/unit/loginTest.tsx b/tests/unit/loginTest.tsx index d5084299bb08..21b0bdd528f6 100644 --- a/tests/unit/loginTest.tsx +++ b/tests/unit/loginTest.tsx @@ -4,6 +4,11 @@ import 'react-native'; import renderer from 'react-test-renderer'; import App from '@src/App'; +jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ // eslint-disable-next-line @typescript-eslint/naming-convention From 310e010e902a1bda03a4c0dd3ea9b62101adf0d3 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Wed, 8 Jan 2025 09:52:30 +0100 Subject: [PATCH 30/33] feat: add a log when executing background task --- src/setup/backgroundTask/index.ios.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/setup/backgroundTask/index.ios.ts b/src/setup/backgroundTask/index.ios.ts index f73d5f9a11cd..52f8c1d9a66e 100644 --- a/src/setup/backgroundTask/index.ios.ts +++ b/src/setup/backgroundTask/index.ios.ts @@ -1,8 +1,10 @@ import TaskManager from '@expensify/react-native-background-task'; +import Log from '@libs/Log'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; const BACKGROUND_FETCH_TASK = 'FLUSH-SEQUENTIAL-QUEUE-BACKGROUND-FETCH'; TaskManager.defineTask(BACKGROUND_FETCH_TASK, () => { + Log.info('BackgroundTask', true, `Executing ${BACKGROUND_FETCH_TASK} background task at ${new Date().toISOString()}`); SequentialQueue.flush(); }); From a1c707e814129453786d5bc3d577e68628932a49 Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Mon, 13 Jan 2025 20:56:03 +0100 Subject: [PATCH 31/33] fix: move mocks to one file --- jest/setup.ts | 5 +++++ tests/ui/GroupChatNameTests.tsx | 5 ----- tests/ui/PaginationTest.tsx | 5 ----- tests/ui/SwitchToExpensifyClassicTest.tsx | 5 ----- tests/ui/UnreadIndicatorsTest.tsx | 5 ----- tests/ui/WorkspaceSwitcherTest.tsx | 5 ----- tests/unit/loginTest.tsx | 5 ----- 7 files changed, 5 insertions(+), 30 deletions(-) diff --git a/jest/setup.ts b/jest/setup.ts index c575054f7dac..4db3d945ad8f 100644 --- a/jest/setup.ts +++ b/jest/setup.ts @@ -83,6 +83,11 @@ jest.mock('@src/libs/actions/Timing', () => ({ end: jest.fn(), })); +jest.mock('../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ + defineTask: jest.fn(), + onBackgroundTaskExecution: jest.fn(), +})); + // This makes FlatList render synchronously for easier testing. jest.mock( '@react-native/virtualized-lists/Interaction/Batchinator', diff --git a/tests/ui/GroupChatNameTests.tsx b/tests/ui/GroupChatNameTests.tsx index eef08d7cbc40..fc383efe4e28 100644 --- a/tests/ui/GroupChatNameTests.tsx +++ b/tests/ui/GroupChatNameTests.tsx @@ -22,11 +22,6 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(50000); -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - jest.mock('../../src/components/ConfirmedRoute.tsx'); // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest diff --git a/tests/ui/PaginationTest.tsx b/tests/ui/PaginationTest.tsx index 59e164180075..8fcd6dbac1d6 100644 --- a/tests/ui/PaginationTest.tsx +++ b/tests/ui/PaginationTest.tsx @@ -21,11 +21,6 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - jest.mock('@react-navigation/native'); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); diff --git a/tests/ui/SwitchToExpensifyClassicTest.tsx b/tests/ui/SwitchToExpensifyClassicTest.tsx index 35c5ec1e58bb..c8fe13b7e2e9 100644 --- a/tests/ui/SwitchToExpensifyClassicTest.tsx +++ b/tests/ui/SwitchToExpensifyClassicTest.tsx @@ -16,11 +16,6 @@ const USER_A_EMAIL = 'user_a@test.com'; jest.setTimeout(60000); -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - jest.mock('@react-navigation/native'); TestHelper.setupApp(); diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index 9509ee81db0d..6f4313a9f02c 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -32,11 +32,6 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - jest.mock('@react-navigation/native'); jest.mock('../../src/libs/Notification/LocalNotification'); jest.mock('../../src/components/Icon/Expensicons'); diff --git a/tests/ui/WorkspaceSwitcherTest.tsx b/tests/ui/WorkspaceSwitcherTest.tsx index 3ae442823726..614ed4e5ab70 100644 --- a/tests/ui/WorkspaceSwitcherTest.tsx +++ b/tests/ui/WorkspaceSwitcherTest.tsx @@ -18,11 +18,6 @@ import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct' // We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App jest.setTimeout(60000); -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - jest.mock('@react-navigation/native', () => { const actualNav = jest.requireActual('@react-navigation/native'); return { diff --git a/tests/unit/loginTest.tsx b/tests/unit/loginTest.tsx index 21b0bdd528f6..d5084299bb08 100644 --- a/tests/unit/loginTest.tsx +++ b/tests/unit/loginTest.tsx @@ -4,11 +4,6 @@ import 'react-native'; import renderer from 'react-test-renderer'; import App from '@src/App'; -jest.mock('../../modules/background-task/src/NativeReactNativeBackgroundTask', () => ({ - defineTask: jest.fn(), - onBackgroundTaskExecution: jest.fn(), -})); - // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ // eslint-disable-next-line @typescript-eslint/naming-convention From 8a914f3e9a817157d6ee110d03952bd8cf61f33b Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Mon, 13 Jan 2025 21:52:57 +0100 Subject: [PATCH 32/33] feat: pass error to JS --- .../ios/ReactNativeBackgroundTask.mm | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm index 8ac39f3707e5..f963c928a41e 100644 --- a/modules/background-task/ios/ReactNativeBackgroundTask.mm +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -16,7 +16,7 @@ - (instancetype)init { return self; } -- (BOOL)scheduleNewBackgroundTask:(NSString *)identifier { +- (BOOL)scheduleNewBackgroundTask:(NSString *)identifier error:(NSError **)outError { BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:identifier]; // Set earliest begin date to some time in the future @@ -27,6 +27,9 @@ - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier { if (!success) { NSLog(@"[ReactNativeBackgroundTask] Failed to schedule task: %@", error.localizedDescription); + if (outError != nil) { + *outError = error; + } } return success; @@ -60,7 +63,8 @@ - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier { NSLog(@"[ReactNativeBackgroundTask] Defining task: %@", taskName); - BOOL allSuccess = YES; + BOOL allSuccess = YES; + NSError *taskError = nil; for (NSString *identifier in backgroundIdentifiers) { [[RNBackgroundTaskManager shared] setHandlerForIdentifier:identifier completion:^(BGTask * _Nonnull task) { @@ -72,17 +76,20 @@ - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier { [self emitOnBackgroundTaskExecution:(taskName)]; }]; - [self scheduleNewBackgroundTask:identifier]; + NSError *scheduleError = nil; + [self scheduleNewBackgroundTask:identifier error:&scheduleError]; [task setTaskCompletedWithSuccess:YES]; }]; - BOOL success = [self scheduleNewBackgroundTask:identifier]; + NSError *scheduleError = nil; + BOOL success = [self scheduleNewBackgroundTask:identifier error:&scheduleError]; if (success) { _taskExecutors[taskName] = taskExecutor; } else { allSuccess = NO; + taskError = scheduleError; break; } } @@ -90,10 +97,11 @@ - (BOOL)scheduleNewBackgroundTask:(NSString *)identifier { if (allSuccess) { resolve(@YES); } else { - reject(@"error", @"Failed to schedule initial background task", nil); + reject(@"ERR_SCHEDULE_TASK_FAILED", + taskError.localizedDescription ?: @"Failed to schedule initial background task", + taskError); } - _taskExecutors[taskName] = taskExecutor; } From 6276845d6566d293e5c7107ae371412ab3a2e65a Mon Sep 17 00:00:00 2001 From: szymonrybczak Date: Tue, 14 Jan 2025 18:37:54 +0100 Subject: [PATCH 33/33] fix: eslint error --- src/setup/backgroundTask/index.ios.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/setup/backgroundTask/index.ios.ts b/src/setup/backgroundTask/index.ios.ts index 52f8c1d9a66e..e616dbb2dcb8 100644 --- a/src/setup/backgroundTask/index.ios.ts +++ b/src/setup/backgroundTask/index.ios.ts @@ -1,10 +1,10 @@ import TaskManager from '@expensify/react-native-background-task'; import Log from '@libs/Log'; -import * as SequentialQueue from '@libs/Network/SequentialQueue'; +import {flush} from '@libs/Network/SequentialQueue'; const BACKGROUND_FETCH_TASK = 'FLUSH-SEQUENTIAL-QUEUE-BACKGROUND-FETCH'; TaskManager.defineTask(BACKGROUND_FETCH_TASK, () => { Log.info('BackgroundTask', true, `Executing ${BACKGROUND_FETCH_TASK} background task at ${new Date().toISOString()}`); - SequentialQueue.flush(); + flush(); });