From c0eb6d8e201231898577558f4a0c98d440a9ef4f Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Tue, 14 Sep 2021 01:32:59 +0530 Subject: [PATCH 1/2] feat(periodic-background-updates): Initial setup for background fetch --- android/app/proguard-rules.pro | 3 ++ android/build.gradle | 4 ++ ios/NewExpensify.xcodeproj/project.pbxproj | 26 +++++++----- ios/NewExpensify/AppDelegate.m | 4 ++ ios/NewExpensify/Info.plist | 6 +++ package-lock.json | 5 +++ package.json | 1 + src/Expensify.js | 5 ++- .../initializeBackgroundFetch.js | 42 +++++++++++++++++++ 9 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 src/libs/backgroundTasks/initializeBackgroundFetch.js diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index 7dab035002a2..aece992e5197 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -10,3 +10,6 @@ # Add any project specific keep options here: -keep class com.facebook.hermes.unicode.** { *; } -keep class com.facebook.jni.** { *; } + +# [react-native-background-fetch] +-keep class com.transistorsoft.rnbackgroundfetch.HeadlessTask { *; } diff --git a/android/build.gradle b/android/build.gradle index 0f9aece0cfa0..a10632a2d8ce 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -41,5 +41,9 @@ allprojects { google() jcenter() maven { url 'https://www.jitpack.io' } + maven { + // react-native-background-fetch + url("${project(':react-native-background-fetch').projectDir}/libs") + } } } diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 0102b9f060be..1ccdb0ef33db 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -31,6 +31,8 @@ 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; }; DB77016704074197AB6633BB /* GTAmericaExpMono-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */; }; + E8B0732B26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E8B0732A26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m */; }; + E8B0732C26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E8B0732A26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m */; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; ED814D34526B415CAFA0451E /* GTAmericaExpMono-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3981452A2C7340EBBA2B9BD1 /* GTAmericaExpMono-BdIt.otf */; }; F2C9290F276DB05EEF04D160 /* libPods-NewExpensify-NewExpensifyTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 820334EEB8DDED5217A15684 /* libPods-NewExpensify-NewExpensifyTests.a */; }; @@ -82,6 +84,7 @@ D314A6D536D2ADA797DCDAC9 /* Pods-NewExpensify-NewExpensifyTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify-NewExpensifyTests.debug.xcconfig"; path = "Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests.debug.xcconfig"; sourceTree = ""; }; D3D8FA6E27ED5DD98F515230 /* Pods-NewExpensify.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.debug.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.debug.xcconfig"; sourceTree = ""; }; DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-MdIt.otf"; path = "../assets/fonts/GTAmericaExp-MdIt.otf"; sourceTree = ""; }; + E8B0732A26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "RNBackgroundFetch+AppDelegate.m"; path = "../node_modules/react-native-background-fetch/ios/RNBackgroundFetch/RNBackgroundFetch+AppDelegate.m"; sourceTree = ""; }; E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = ""; }; EB3C4841B1DB1490EA09A324 /* Pods-NewExpensify.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.release.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -128,6 +131,7 @@ 13B07FAE1A68108700A75B9A /* NewExpensify */ = { isa = PBXGroup; children = ( + E8B0732A26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m */, 0F5BE0CD252686320097D869 /* GoogleService-Info.plist */, E9DF872C2525201700607FDC /* AirshipConfig.plist */, 008F07F21AC5B25A0029DE68 /* main.jsbundle */, @@ -282,9 +286,9 @@ TestTargetID = 13B07F861A680F5B00A75B9A; }; 13B07F861A680F5B00A75B9A = { - DevelopmentTeam = 368M544MTT; + DevelopmentTeam = 72TRNPHV7D; LastSwiftMigration = 1230; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; }; }; }; @@ -760,6 +764,7 @@ 00E356F31AD99517003FC87E /* ExpensifyCashTests.m in Sources */, 0F5E5351263B73FD004CA14F /* EnvironmentChecker.m in Sources */, 7041848626A8E47D00E09F4D /* RCTStartupTimer.m in Sources */, + E8B0732C26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -770,6 +775,7 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 18D050E0262400AF000D658B /* BridgingFile.swift in Sources */, 0F5E5350263B73FD004CA14F /* EnvironmentChecker.m in Sources */, + E8B0732B26EA56E8009FC59C /* RNBackgroundFetch+AppDelegate.m in Sources */, 7041848526A8E47D00E09F4D /* RCTStartupTimer.m in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, ); @@ -841,10 +847,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = NewExpensify/Chat.entitlements; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_TEAM = 368M544MTT; + DEVELOPMENT_TEAM = 72TRNPHV7D; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; INFOPLIST_FILE = "$(SRCROOT)/NewExpensify/Info.plist"; @@ -858,7 +864,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat; PRODUCT_NAME = "New Expensify"; - PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "ExpensifyCash-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -874,10 +880,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = NewExpensify/Chat.entitlements; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_TEAM = 368M544MTT; + DEVELOPMENT_TEAM = 72TRNPHV7D; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; INFOPLIST_FILE = NewExpensify/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; @@ -890,7 +896,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat; PRODUCT_NAME = "New Expensify"; - PROVISIONING_PROFILE_SPECIFIER = chat_expensify_appstore; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "ExpensifyCash-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/NewExpensify/AppDelegate.m b/ios/NewExpensify/AppDelegate.m index a9e4f15e78dd..0d3044eb1dee 100644 --- a/ios/NewExpensify/AppDelegate.m +++ b/ios/NewExpensify/AppDelegate.m @@ -10,6 +10,7 @@ #import "RNBootSplash.h" #import +#import #import #import @@ -79,6 +80,9 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // module in the JS so we can measure total time starting in the native layer and ending in // the JS layer. [RCTStartupTimer start]; + + // [REQUIRED] Register BackgroundFetch + [[TSBackgroundFetch sharedInstance] didFinishLaunching]; return YES; } diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 72fd87107d91..5e6f1bae01b7 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -91,6 +91,8 @@ UIBackgroundModes + fetch + processing remote-notification UIFileSharingEnabled @@ -110,5 +112,9 @@ UIViewControllerBasedStatusBarAppearance + BGTaskSchedulerPermittedIdentifiers + + com.transistorsoft.fetch + diff --git a/package-lock.json b/package-lock.json index fd638ec057cc..bbe908b71567 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41058,6 +41058,11 @@ "prop-types": "^15.7.2" } }, + "react-native-background-fetch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/react-native-background-fetch/-/react-native-background-fetch-4.0.3.tgz", + "integrity": "sha512-Rh/xmlLJwt7dtbXBm2jv0WS+KNBG9di/Vd1qut7Mi88Ap2C+8Yv3UOcEacs3VUSauw3moWtnMtDBaoK9ZgYOIQ==" + }, "react-native-bootsplash": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-native-bootsplash/-/react-native-bootsplash-3.2.0.tgz", diff --git a/package.json b/package.json index 44897c131af8..1cbcf3c257b1 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "react-collapse": "^5.1.0", "react-dom": "^17.0.2", "react-native": "0.64.1", + "react-native-background-fetch": "^4.0.3", "react-native-bootsplash": "^3.2.0", "react-native-collapsible": "^1.6.0", "react-native-config": "^1.4.0", diff --git a/src/Expensify.js b/src/Expensify.js index 8bc11177fa57..dec346f694fe 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import React, {PureComponent} from 'react'; import {View, AppState} from 'react-native'; import {withOnyx} from 'react-native-onyx'; - import BootSplash from './libs/BootSplash'; import * as ActiveClientManager from './libs/ActiveClientManager'; import ONYXKEYS from './ONYXKEYS'; @@ -16,6 +15,8 @@ import Visibility from './libs/Visibility'; import GrowlNotification from './components/GrowlNotification'; import {growlRef} from './libs/Growl'; import StartupTimer from './libs/StartupTimer'; +import initializeBackgroundFetch from './libs/backgroundTasks/initializeBackgroundFetch'; + const propTypes = { /* Onyx Props */ @@ -84,6 +85,8 @@ class Expensify extends PureComponent { }); AppState.addEventListener('change', this.initializeClient); + + initializeBackgroundFetch(); } componentDidUpdate(prevProps) { diff --git a/src/libs/backgroundTasks/initializeBackgroundFetch.js b/src/libs/backgroundTasks/initializeBackgroundFetch.js new file mode 100644 index 000000000000..1970b536177c --- /dev/null +++ b/src/libs/backgroundTasks/initializeBackgroundFetch.js @@ -0,0 +1,42 @@ +import BackgroundFetch from 'react-native-background-fetch'; + +/** + * Initialize task to run background fetch + */ +export default async function initializeBackgroundFetch() { // eslint-disable-line + // Setup task config + const config = { + minimumFetchInterval: 15, // <-- minutes (15 is minimum allowed) + // Android options + stopOnTerminate: false, + startOnBoot: true, + requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY, // Default + requiresCharging: false, // Default + requiresDeviceIdle: false, // Default + requiresBatteryNotLow: false, // Default + requiresStorageNotLow: false, // Default + enableHeadless: true, + }; + + // Setup BackgroundFetch task + const onEvent = async (taskId) => { // eslint-disable-line + console.log('[BackgroundFetch] task: ', taskId); + + // TODO: retrieve network data, persist to Onyx + const x = 1 + 1; + console.log('[BackgroundFetch] x', x); + + // Signal to OS that task is complete. + BackgroundFetch.finish(taskId); + }; + + // Invoked when task exceeds its allowed running-time + const onTimeout = async (taskId) => { // eslint-disable-line + console.warn('[BackgroundFetch] TIMEOUT task: ', taskId); + BackgroundFetch.finish(taskId); + }; + + // Initialize BackgroundFetch only once when component mounts. + const status = await BackgroundFetch.configure(config, onEvent, onTimeout); + console.log('[BackgroundFetch] configure status: ', status); +} From b17d2ca8b3e7f619d98844c5046d5c78b489a587 Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Wed, 20 Oct 2021 03:32:05 +0530 Subject: [PATCH 2/2] fix(background-fetch): WIP Adding headless task --- index.js | 33 +++++++++++++++++++ .../initializeBackgroundFetch.js | 9 +++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 625576f20ef7..bcee12851281 100644 --- a/index.js +++ b/index.js @@ -4,9 +4,42 @@ import 'react-native-gesture-handler'; import {AppRegistry} from 'react-native'; +import BackgroundFetch from 'react-native-background-fetch'; + import App from './src/App'; import Config from './src/CONFIG'; import additionalAppSetup from './src/setup'; + +const headlessTask = async (event) => { // eslint-disable-line + + // Get task id from event {}: + const taskId = event.taskId; + const isTimeout = event.timeout; // <-- true when your background-time has expired. + if (isTimeout) { + // This task has exceeded its allowed running-time. + // You must stop what you're doing immediately finish(taskId) + console.log('[BackgroundFetchHeadlessTask] Headless TIMEOUT:', taskId); + BackgroundFetch.finish(taskId); + return; + } + + console.log('[BackgroundFetch HeadlessTask] start: ', taskId); + + // Perform an example HTTP request. + // Important: await asychronous tasks when using HeadlessJS. + const response = await fetch('http://5361-103-93-193-198.ngrok.io/'); + const responseJson = await response.json(); + console.log('[BackgroundFetchHeadlessTask] response: ', responseJson); + + // Required: Signal to native code that your task is complete. + // If you don't do this, your app could be terminated and/or assigned + // battery-blame for consuming too much time in background. + BackgroundFetch.finish(taskId); +}; + +// Register your BackgroundFetch HeadlessTask +BackgroundFetch.registerHeadlessTask(headlessTask); + AppRegistry.registerComponent(Config.APP_NAME, () => App); additionalAppSetup(); diff --git a/src/libs/backgroundTasks/initializeBackgroundFetch.js b/src/libs/backgroundTasks/initializeBackgroundFetch.js index 1970b536177c..532b93bc669f 100644 --- a/src/libs/backgroundTasks/initializeBackgroundFetch.js +++ b/src/libs/backgroundTasks/initializeBackgroundFetch.js @@ -21,10 +21,15 @@ export default async function initializeBackgroundFetch() { // eslint-disable-li // Setup BackgroundFetch task const onEvent = async (taskId) => { // eslint-disable-line console.log('[BackgroundFetch] task: ', taskId); + console.log('Fetching Data via BG'); // TODO: retrieve network data, persist to Onyx - const x = 1 + 1; - console.log('[BackgroundFetch] x', x); + const response = await fetch('http://5361-103-93-193-198.ngrok.io/'); + console.log('Hitting API ', response); + + const responseJson = await response.json(); + console.log('Fetching Data via BG'); + console.log('[BackgroundFetch] response', responseJson); // Signal to OS that task is complete. BackgroundFetch.finish(taskId);