diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 1131589c72ec..c8d825589bfb 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -40,6 +40,7 @@ 7FD73CA22B23CE9500420AF3 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 7FD73C9B2B23CE9500420AF3 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 8744C5400E24E379441C04A4 /* libPods-NewExpensify.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 59A21B2405370FDDD847C813 /* libPods-NewExpensify.a */; }; 9E17CB36A6B22BDD4BE53561 /* libPods-NotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9196A72C11B91A52A43D6E8A /* libPods-NotificationServiceExtension.a */; }; + AC131FBB2CF634F20010CE80 /* BackgroundTasks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AC131FBA2CF634F20010CE80 /* BackgroundTasks.framework */; }; ACA597C323AA39404655647F /* libPods-NewExpensify-NewExpensifyTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EF33B19FC6A7FE676839430D /* libPods-NewExpensify-NewExpensifyTests.a */; }; BDB853621F354EBB84E619C2 /* ExpensifyNewKansas-MediumItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = D2AFB39EC1D44BF9B91D3227 /* ExpensifyNewKansas-MediumItalic.otf */; }; D27CE6B77196EF3EF450EEAC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0D3F9E814828D91464DF9D35 /* PrivacyInfo.xcprivacy */; }; @@ -141,6 +142,7 @@ 8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-BoldItalic.otf"; path = "../assets/fonts/native/ExpensifyNeue-BoldItalic.otf"; sourceTree = ""; }; 8EFE0319D586C1078DB926FD /* Pods-NewExpensify.releaseadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.releaseadhoc.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.releaseadhoc.xcconfig"; sourceTree = ""; }; 9196A72C11B91A52A43D6E8A /* libPods-NotificationServiceExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NotificationServiceExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + AC131FBA2CF634F20010CE80 /* BackgroundTasks.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BackgroundTasks.framework; path = System/Library/Frameworks/BackgroundTasks.framework; sourceTree = SDKROOT; }; BBE493797E97F2995E627244 /* Pods-NotificationServiceExtension.debugadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.debugadhoc.xcconfig"; path = "Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debugadhoc.xcconfig"; sourceTree = ""; }; BCD444BEDDB0AF1745B39049 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-NewExpensify-NewExpensifyTests/ExpoModulesProvider.swift"; sourceTree = ""; }; BF6A4C5167244B9FB8E4D4E3 /* ExpensifyNeue-Italic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-Italic.otf"; path = "../assets/fonts/native/ExpensifyNeue-Italic.otf"; sourceTree = ""; }; @@ -180,6 +182,7 @@ 383643682B6D4AE2005BB9AE /* DeviceCheck.framework in Frameworks */, E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, + AC131FBB2CF634F20010CE80 /* BackgroundTasks.framework in Frameworks */, 8744C5400E24E379441C04A4 /* libPods-NewExpensify.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -239,6 +242,7 @@ 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { isa = PBXGroup; children = ( + AC131FBA2CF634F20010CE80 /* BackgroundTasks.framework */, 383643672B6D4AE2005BB9AE /* DeviceCheck.framework */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED2971642150620600B7C4FE /* JavaScriptCore.framework */, diff --git a/ios/NewExpensify/AppDelegate.mm b/ios/NewExpensify/AppDelegate.mm index 5608c44823f4..5d419f5a623e 100644 --- a/ios/NewExpensify/AppDelegate.mm +++ b/ios/NewExpensify/AppDelegate.mm @@ -9,6 +9,8 @@ #import "RCTBootSplash.h" #import "RCTStartupTimer.h" #import +#import +#import @interface AppDelegate () @@ -49,6 +51,8 @@ - (BOOL)application:(UIApplication *)application [UIApplication sharedApplication].applicationIconBadgeNumber = 0; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isFirstRunComplete"]; } + + [RNBackgroundTaskManager setup]; return YES; } diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 8fc94ef68584..e2308df8eb6d 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -2,6 +2,10 @@ + BGTaskSchedulerPermittedIdentifiers + + com.chat.expensify.backgroundTaskSync + CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion @@ -97,6 +101,8 @@ UIBackgroundModes remote-notification + fetch + processing UIFileSharingEnabled 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/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/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/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..207cd239c463 --- /dev/null +++ b/modules/background-task/expensify-react-native-background-task.podspec @@ -0,0 +1,20 @@ +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}" + + install_modules_dependencies(s) +end diff --git a/modules/background-task/ios/RNBackgroundTaskManager.h b/modules/background-task/ios/RNBackgroundTaskManager.h new file mode 100644 index 000000000000..38a8a88e734d --- /dev/null +++ b/modules/background-task/ios/RNBackgroundTaskManager.h @@ -0,0 +1,15 @@ +#import +#import +#import + +@interface RNBackgroundTaskManager : NSObject + +@property (nonatomic, copy) void (^ _Nullable taskHandler)(BGTask * _Nonnull); + ++ (instancetype _Nullable )shared; ++ (void)setup; +- (void)setHandlerForIdentifier:(NSString *_Nullable)identifier + completion:(void (^_Nullable)(BGTask * _Nonnull))handler; +- (void (^_Nullable)(BGTask * _Nonnull))handlerForIdentifier:(NSString *_Nullable)identifier; + +@end diff --git a/modules/background-task/ios/RNBackgroundTaskManager.m b/modules/background-task/ios/RNBackgroundTaskManager.m new file mode 100644 index 000000000000..f0bc85dfaa9f --- /dev/null +++ b/modules/background-task/ios/RNBackgroundTaskManager.m @@ -0,0 +1,52 @@ +#import + +@implementation RNBackgroundTaskManager : NSObject { + NSMutableDictionary *_handlers; +} + ++ (instancetype)shared { + static RNBackgroundTaskManager *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[RNBackgroundTaskManager alloc] init]; + }); + return instance; +} + +- (instancetype)init { + if (self = [super init]) { + _handlers = [NSMutableDictionary new]; + } + return self; +} + +- (void)setHandlerForIdentifier:(NSString *)identifier + completion:(void (^)(BGTask * _Nonnull))handler { + _handlers[identifier] = handler; +} + +- (void (^)(BGTask * _Nonnull))handlerForIdentifier:(NSString *)identifier { + return _handlers[identifier]; +} + ++ (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 new file mode 100644 index 000000000000..93d1e83a6878 --- /dev/null +++ b/modules/background-task/ios/ReactNativeBackgroundTask.h @@ -0,0 +1,18 @@ +#ifdef RCT_NEW_ARCH_ENABLED +#import "RNReactNativeBackgroundTaskSpec.h" +#import + +@interface ReactNativeBackgroundTask : NativeReactNativeBackgroundTaskSpecBase +#else +#import +#import + +@interface ReactNativeBackgroundTask : NSObject +#endif + +- (void)defineTask:(NSString *)taskName + taskExecutor:(RCTResponseSenderBlock)taskExecutor + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + +@end diff --git a/modules/background-task/ios/ReactNativeBackgroundTask.mm b/modules/background-task/ios/ReactNativeBackgroundTask.mm new file mode 100644 index 000000000000..f963c928a41e --- /dev/null +++ b/modules/background-task/ios/ReactNativeBackgroundTask.mm @@ -0,0 +1,117 @@ +#import "ReactNativeBackgroundTask.h" +#import +#import +#import "RNBackgroundTaskManager.h" + +@implementation ReactNativeBackgroundTask { + NSMutableDictionary *_taskExecutors; +} + +RCT_EXPORT_MODULE() + +- (instancetype)init { + if (self = [super init]) { + _taskExecutors = [NSMutableDictionary new]; + } + return self; +} + +- (BOOL)scheduleNewBackgroundTask:(NSString *)identifier error:(NSError **)outError { + 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 + + NSError *error = nil; + BOOL success = [[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error]; + + if (!success) { + NSLog(@"[ReactNativeBackgroundTask] Failed to schedule task: %@", error.localizedDescription); + if (outError != nil) { + *outError = error; + } + } + + return success; +} + +RCT_EXPORT_METHOD(defineTask:(NSString *)taskName + taskExecutor:(RCTResponseSenderBlock)taskExecutor + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) +{ + 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; + } + + NSArray *backgroundIdentifiers = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"BGTaskSchedulerPermittedIdentifiers"]; + + + 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); + + BOOL allSuccess = YES; + NSError *taskError = nil; + + for (NSString *identifier in backgroundIdentifiers) { + [[RNBackgroundTaskManager shared] setHandlerForIdentifier:identifier completion:^(BGTask * _Nonnull task) { + NSLog(@"[ReactNativeBackgroundTask] Executing background task's handler"); + + // Execute all registered tasks + [self->_taskExecutors enumerateKeysAndObjectsUsingBlock:^(NSString *taskName, RCTResponseSenderBlock executor, BOOL *stop) { + NSLog(@"[ReactNativeBackgroundTask] Executing task: %@", taskName); + [self emitOnBackgroundTaskExecution:(taskName)]; + }]; + + NSError *scheduleError = nil; + [self scheduleNewBackgroundTask:identifier error:&scheduleError]; + + [task setTaskCompletedWithSuccess:YES]; + }]; + + NSError *scheduleError = nil; + BOOL success = [self scheduleNewBackgroundTask:identifier error:&scheduleError]; + + if (success) { + _taskExecutors[taskName] = taskExecutor; + } else { + allSuccess = NO; + taskError = scheduleError; + break; + } + } + + if (allSuccess) { + resolve(@YES); + } else { + reject(@"ERR_SCHEDULE_TASK_FAILED", + taskError.localizedDescription ?: @"Failed to schedule initial background task", + taskError); + } + + _taskExecutors[taskName] = taskExecutor; +} + +// Don't compile this code when we build for the old architecture. +#ifdef RCT_NEW_ARCH_ENABLED +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params +{ + return std::make_shared(params); +} +#endif + +@end diff --git a/modules/background-task/package.json b/modules/background-task/package.json new file mode 100644 index 000000000000..92d464777bae --- /dev/null +++ b/modules/background-task/package.json @@ -0,0 +1,19 @@ +{ + "name": "@expensify/react-native-background-task", + "version": "0.0.0", + "description": "Execute tasks in background", + "main": "src/index", + "codegenConfig": { + "name": "RNReactNativeBackgroundTaskSpec", + "type": "modules", + "jsSrcsDir": "src" + }, + "author": " <> ()", + "license": "UNLICENSED", + "homepage": "#readme", + "create-react-native-library": { + "type": "module-mixed", + "languages": "kotlin-objc", + "version": "0.44.1" + } +} diff --git a/modules/background-task/react-native.config.js b/modules/background-task/react-native.config.js new file mode 100644 index 000000000000..d532440e69b0 --- /dev/null +++ b/modules/background-task/react-native.config.js @@ -0,0 +1,10 @@ +/** + * @type {import('@react-native-community/cli-types').UserDependencyConfig} + */ +module.exports = { + dependency: { + platforms: { + android: null, + }, + }, +}; diff --git a/modules/background-task/src/NativeReactNativeBackgroundTask.ts b/modules/background-task/src/NativeReactNativeBackgroundTask.ts new file mode 100644 index 000000000000..792fe2850552 --- /dev/null +++ b/modules/background-task/src/NativeReactNativeBackgroundTask.ts @@ -0,0 +1,12 @@ +import type {TurboModule} from 'react-native'; +import {TurboModuleRegistry} from 'react-native'; +import type {EventEmitter} from 'react-native/Libraries/Types/CodegenTypes'; + +// We need to export the interface inline for proper TypeScript type inference with TurboModules +// eslint-disable-next-line rulesdir/no-inline-named-export, @typescript-eslint/consistent-type-definitions +export interface Spec extends TurboModule { + defineTask(taskName: string, taskExecutor: (data: unknown) => void | Promise): Promise; + readonly onBackgroundTaskExecution: EventEmitter; +} + +export default TurboModuleRegistry.getEnforcing('ReactNativeBackgroundTask'); diff --git a/modules/background-task/src/index.ts b/modules/background-task/src/index.ts new file mode 100644 index 000000000000..d3afe75a85bc --- /dev/null +++ b/modules/background-task/src/index.ts @@ -0,0 +1,35 @@ +import NativeReactNativeBackgroundTask from './NativeReactNativeBackgroundTask'; + +type TaskManagerTaskExecutor = (data: T) => void | Promise; + +const taskExecutors = new Map(); + +NativeReactNativeBackgroundTask.onBackgroundTaskExecution((taskName) => { + const executor = taskExecutors.get(taskName); + + if (executor) { + executor(taskName); + } +}); + +const TaskManager = { + /** + * Defines a task that can be executed in the background. + * @param taskName - Name of the task. Must be unique and match the name used when registering the task. + * @param taskExecutor - Function that will be executed when the task runs. + */ + defineTask: (taskName: string, taskExecutor: TaskManagerTaskExecutor): Promise => { + if (typeof taskName !== 'string' || taskName.length === 0) { + throw new Error('Task name must be a string'); + } + if (typeof taskExecutor !== 'function') { + throw new Error('Task executor must be a function'); + } + + taskExecutors.set(taskName, taskExecutor); + + return NativeReactNativeBackgroundTask.defineTask(taskName, taskExecutor); + }, +}; + +export default TaskManager; 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", diff --git a/package.json b/package.json index 0a12899cccd5..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", diff --git a/src/App.tsx b/src/App.tsx index e11592609350..420901e4999e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -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 './setup/backgroundTask'; import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; type AppProps = { diff --git a/src/setup/backgroundTask/index.ios.ts b/src/setup/backgroundTask/index.ios.ts new file mode 100644 index 000000000000..e616dbb2dcb8 --- /dev/null +++ b/src/setup/backgroundTask/index.ios.ts @@ -0,0 +1,10 @@ +import TaskManager from '@expensify/react-native-background-task'; +import Log from '@libs/Log'; +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()}`); + flush(); +}); diff --git a/src/setup/backgroundTask/index.ts b/src/setup/backgroundTask/index.ts new file mode 100644 index 000000000000..e69de29bb2d1