From e7ca379f714a27e776fe5891caeb61946bfc4eef Mon Sep 17 00:00:00 2001
From: szymonrybczak <szymon.rybczak@gmail.com>
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 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.expensify.reactnativebackgroundtask">
+</manifest>
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 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+</manifest>
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<String, ReactModuleInfo> = 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 <NativeReactNativeBackgroundTaskSpec>
+#else
+#import <React/RCTBridgeModule.h>
+
+@interface ReactNativeBackgroundTask : NSObject <RCTBridgeModule>
+#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<facebook::react::TurboModule>)getTurboModule:
+    (const facebook::react::ObjCTurboModule::InitParams &)params
+{
+    return std::make_shared<facebook::react::NativeReactNativeBackgroundTaskSpecJSI>(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<number>;
+}
+
+export default TurboModuleRegistry.getEnforcing<Spec>('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<number> {
+  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 <szymon.rybczak@gmail.com>
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 <RCTBridgeModule>
 #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<number>;
+    defineTask(taskName: string, taskExecutor: (data: unknown) => void | Promise<void>): Promise<void>;
 }
 
 export default TurboModuleRegistry.getEnforcing<Spec>('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<T = unknown> = (data: T) => void | Promise<void>;
+
+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<void> => {
+        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<number> {
-  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 (
         <StrictModeWrapper>
             <SplashScreenStateContextProvider>
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 <szymon.rybczak@gmail.com>
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 @@
 	<key>UIBackgroundModes</key>
 	<array>
 		<string>remote-notification</string>
+		<string>fetch</string>
+		<string>processing</string>
 	</array>
 	<key>UIFileSharingEnabled</key>
 	<true/>
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 <BackgroundTasks/BackgroundTasks.h>
 
 @interface ReactNativeBackgroundTask : NSObject <NativeReactNativeBackgroundTaskSpec>
 #else
 #import <React/RCTBridgeModule.h>
+#import <BackgroundTasks/BackgroundTasks.h>
 
 @interface ReactNativeBackgroundTask : NSObject <RCTBridgeModule>
 #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 <UIKit/UIKit.h>
+#import <BackgroundTasks/BackgroundTasks.h>
 
 @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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 = "<group>"; };
 		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 = "<group>"; };
 		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 = "<group>"; };
 		BCD444BEDDB0AF1745B39049 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-NewExpensify-NewExpensifyTests/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
 		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 = "<group>"; };
@@ -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 <HardwareShortcuts.h>
+#import <BackgroundTasks/BackgroundTasks.h>
+#import <expensify-react-native-background-task/RNBackgroundTaskManager.h>
 
 @interface AppDelegate () <UNUserNotificationCenterDelegate>
 
@@ -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 @@
 		<string>fetch</string>
 		<string>processing</string>
 	</array>
+  <key>BGTaskSchedulerPermittedIdentifiers</key>
+  <array>
+      <string>com.szymonrybczak.chat</string>
+  </array>
 	<key>UIFileSharingEnabled</key>
 	<true/>
 	<key>UILaunchStoryboardName</key>
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 <Foundation/Foundation.h>
+#import <BackgroundTasks/BackgroundTasks.h>
+#import <React/RCTBridgeModule.h>
+
+@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 <BackgroundTasks/BackgroundTasks.h>
+
+// RNBackgroundTaskManager.m
+@implementation RNBackgroundTaskManager : NSObject  {
+    NSMutableDictionary<NSString *, void (^)(BGTask * _Nonnull)> *_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 <UIKit/UIKit.h>
 #import <BackgroundTasks/BackgroundTasks.h>
+#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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 (
         <StrictModeWrapper>
             <SplashScreenStateContextProvider>
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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.expensify.reactnativebackgroundtask">
-</manifest>
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 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-</manifest>
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<String, ReactModuleInfo> = 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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 <Foundation/Foundation.h>
 #import <BackgroundTasks/BackgroundTasks.h>
 #import <React/RCTBridgeModule.h>
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 <BackgroundTasks/BackgroundTasks.h>
 
-// RNBackgroundTaskManager.m
 @implementation RNBackgroundTaskManager : NSObject  {
     NSMutableDictionary<NSString *, void (^)(BGTask * _Nonnull)> *_handlers;
 }

From 5d2abd162d1b13e050fd2a48ec682db0df25fed3 Mon Sep 17 00:00:00 2001
From: szymonrybczak <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 <BackgroundTasks/BackgroundTasks.h>
 
-@interface ReactNativeBackgroundTask : NSObject <NativeReactNativeBackgroundTaskSpec>
+@interface ReactNativeBackgroundTask : NativeReactNativeBackgroundTaskSpecBase <NativeReactNativeBackgroundTaskSpec>
 #else
 #import <React/RCTBridgeModule.h>
 #import <BackgroundTasks/BackgroundTasks.h>
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<void>): Promise<void>;
+    readonly onBackgroundTaskExecution: EventEmitter<string>;
 }
 
 export default TurboModuleRegistry.getEnforcing<Spec>('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<T = unknown> = (data: T) => void | Promise<void>;
 
+const taskExecutors = new Map<string, TaskManagerTaskExecutor>();
+
+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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 @@
 		<string>fetch</string>
 		<string>processing</string>
 	</array>
-  <key>BGTaskSchedulerPermittedIdentifiers</key>
-  <array>
-      <string>com.szymonrybczak.chat</string>
-  </array>
+	<key>BGTaskSchedulerPermittedIdentifiers</key>
+	<array>
+		<string>com.expensify.chat.dev</string>
+	</array>
 	<key>UIFileSharingEnabled</key>
 	<true/>
 	<key>UILaunchStoryboardName</key>

From a9d575503cc84e3204a73bb1e7cd24a5829eff31 Mon Sep 17 00:00:00 2001
From: szymonrybczak <szymon.rybczak@gmail.com>
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 @@
 	</array>
 	<key>BGTaskSchedulerPermittedIdentifiers</key>
 	<array>
+		<string>com.expensify.chat.chat</string>
 		<string>com.expensify.chat.dev</string>
 	</array>
 	<key>UIFileSharingEnabled</key>

From e663e606761ccdaf446c2dc136895176bd9b599f Mon Sep 17 00:00:00 2001
From: szymonrybczak <szymon.rybczak@gmail.com>
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 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>BGTaskSchedulerPermittedIdentifiers</key>
+	<array>
+		<string>com.chat.expensify.chat</string>
+		<string>com.expensify.chat.dev</string>
+		<string>com.expensify.chat.adhoc</string>
+	</array>
 	<key>CADisableMinimumFrameDurationOnPhone</key>
 	<true/>
 	<key>CFBundleDevelopmentRegion</key>
@@ -100,11 +106,6 @@
 		<string>fetch</string>
 		<string>processing</string>
 	</array>
-	<key>BGTaskSchedulerPermittedIdentifiers</key>
-	<array>
-		<string>com.expensify.chat.chat</string>
-		<string>com.expensify.chat.dev</string>
-	</array>
 	<key>UIFileSharingEnabled</key>
 	<true/>
 	<key>UILaunchStoryboardName</key>

From 1b252dd6c585323d3254d3833ce8e7ed50466661 Mon Sep 17 00:00:00 2001
From: szymonrybczak <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 @@
 <dict>
 	<key>BGTaskSchedulerPermittedIdentifiers</key>
 	<array>
-		<string>com.chat.expensify.chat</string>
-		<string>com.expensify.chat.dev</string>
-		<string>com.expensify.chat.adhoc</string>
+		<string>com.chat.expensify.backgroundTaskSync</string>
 	</array>
 	<key>CADisableMinimumFrameDurationOnPhone</key>
 	<true/>

From a062fc93f786a9a096d7ae29c2b011dbfb5e1057 Mon Sep 17 00:00:00 2001
From: szymonrybczak <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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<void>): Promise<void>;
     readonly onBackgroundTaskExecution: EventEmitter<string>;

From 74d6767106392522a0a44775cd53fd35afb77df7 Mon Sep 17 00:00:00 2001
From: szymonrybczak <szymon.rybczak@gmail.com>
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<typeof Navigation>('@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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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<typeof Navigation>('@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 <szymon.rybczak@gmail.com>
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 <szymon.rybczak@gmail.com>
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();
 });
