diff --git a/android/app/build.gradle b/android/app/build.gradle index fd124e105c38..bb9bb30a2c19 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: "com.android.application" +apply plugin: "com.google.firebase.firebase-perf" apply from: '../../node_modules/react-native-unimodules/gradle.groovy' apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" @@ -233,6 +234,9 @@ dependencies { implementation jscFlavor } + implementation platform("com.google.firebase:firebase-bom:20.0.2") + implementation "com.google.firebase:firebase-perf" + // GIF support implementation 'com.facebook.fresco:fresco:2.3.0' implementation 'com.facebook.fresco:animated-gif:2.3.0' diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 6dc098f4797a..912a6f110cb4 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -4,5 +4,10 @@ - + + + diff --git a/android/app/src/main/java/com/expensify/chat/ExpensifyAppPackage.java b/android/app/src/main/java/com/expensify/chat/ExpensifyAppPackage.java new file mode 100644 index 000000000000..63195d50b2cb --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/ExpensifyAppPackage.java @@ -0,0 +1,29 @@ +package com.expensify.chat; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ExpensifyAppPackage implements ReactPackage { + + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } + + @Override + public List createNativeModules( + ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + + modules.add(new StartupTimer(reactContext)); + + return modules; + } + +} diff --git a/android/app/src/main/java/com/expensify/chat/MainApplication.java b/android/app/src/main/java/com/expensify/chat/MainApplication.java index 824148c2c1b6..0a01f65adc6c 100644 --- a/android/app/src/main/java/com/expensify/chat/MainApplication.java +++ b/android/app/src/main/java/com/expensify/chat/MainApplication.java @@ -42,6 +42,7 @@ protected List getPackages() { // Packages that cannot be autolinked yet can be added manually here, for example: // packages.add(new MyReactNativePackage()); packages.add(new PlaidPackage()); + packages.add(new ExpensifyAppPackage()); // Add unimodules List unimodules = Arrays.asList( @@ -76,6 +77,11 @@ public void onCreate() { FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false); } + // Start the "js_load" custom performance tracing metric. This timer is stopped by a native + // module in the JS so we can measure total time starting in the native layer and ending in + // the JS layer. + StartupTimer.start(); + // Increase SQLite DB write size try { Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize"); diff --git a/android/app/src/main/java/com/expensify/chat/StartupTimer.java b/android/app/src/main/java/com/expensify/chat/StartupTimer.java new file mode 100644 index 000000000000..de43519fcb15 --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/StartupTimer.java @@ -0,0 +1,39 @@ +package com.expensify.chat; +import android.util.Log; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.google.firebase.perf.FirebasePerformance; +import com.google.firebase.perf.metrics.Trace; + +public class StartupTimer extends ReactContextBaseJavaModule { + StartupTimer(ReactApplicationContext context) { + super(context); + } + + private static Trace trace = null; + + @Override + public String getName() { + return "StartupTimer"; + } + + @ReactMethod + public void stop() { + if (trace == null) { + return; + } + + trace.stop(); + } + + public static void start() { + if (BuildConfig.DEBUG) { + Log.d("StartupTimer", "Metric tracing disabled in DEBUG"); + } else { + trace = FirebasePerformance.getInstance().newTrace("js_loaded"); + trace.start(); + } + } +} diff --git a/android/build.gradle b/android/build.gradle index 25095cac7187..66a9c1a64a72 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -18,7 +18,8 @@ buildscript { dependencies { classpath("com.android.tools.build:gradle:4.1.0") classpath("com.google.gms:google-services:4.3.4") - classpath("com.google.firebase:firebase-crashlytics-gradle:2.3.0") + classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1") + classpath("com.google.firebase:perf-plugin:1.4.0") // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" diff --git a/ios/ExpensifyCash.xcodeproj/project.pbxproj b/ios/ExpensifyCash.xcodeproj/project.pbxproj index 3f740ca957f2..eb03211ebd49 100644 --- a/ios/ExpensifyCash.xcodeproj/project.pbxproj +++ b/ios/ExpensifyCash.xcodeproj/project.pbxproj @@ -24,6 +24,8 @@ 425866037F4C482AAB46CB8B /* GTAmericaExp-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */; }; 52477A09739546F4814EA25F /* GTAmericaExpMono-Bd.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0DE5D096095C41EE96746C9E /* GTAmericaExpMono-Bd.otf */; }; 6856B78873B64C44A92E51DB /* GTAmericaExp-MdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */; }; + 7041848526A8E47D00E09F4D /* RCTStartupTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7041848426A8E47D00E09F4D /* RCTStartupTimer.m */; }; + 7041848626A8E47D00E09F4D /* RCTStartupTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7041848426A8E47D00E09F4D /* RCTStartupTimer.m */; }; 70CF6E82262E297300711ADC /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70CF6E81262E297300711ADC /* BootSplash.storyboard */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; }; @@ -66,6 +68,8 @@ 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-RgIt.otf"; path = "../assets/fonts/GTAmericaExpMono-RgIt.otf"; sourceTree = ""; }; 5E0275D64694B453A3EE297D /* Pods-ExpensifyCash.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash.release.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash.release.xcconfig"; sourceTree = ""; }; 67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = ""; }; + 7041848326A8E40900E09F4D /* RCTStartupTimer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RCTStartupTimer.h; path = ExpensifyCash/RCTStartupTimer.h; sourceTree = ""; }; + 7041848426A8E47D00E09F4D /* RCTStartupTimer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RCTStartupTimer.m; path = ExpensifyCash/RCTStartupTimer.m; sourceTree = ""; }; 70CF6E81262E297300711ADC /* BootSplash.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = BootSplash.storyboard; path = ExpensifyCash/BootSplash.storyboard; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ExpensifyCash/LaunchScreen.storyboard; sourceTree = ""; }; 8437A5A38F2047E0BCCD7C2F /* GTAmericaExpMono-Rg.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-Rg.otf"; path = "../assets/fonts/GTAmericaExpMono-Rg.otf"; sourceTree = ""; }; @@ -161,6 +165,8 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + 7041848326A8E40900E09F4D /* RCTStartupTimer.h */, + 7041848426A8E47D00E09F4D /* RCTStartupTimer.m */, 18D050DF262400AF000D658B /* BridgingFile.swift */, 13B07FAE1A68108700A75B9A /* ExpensifyCash */, 832341AE1AAA6A7D00B99B32 /* Libraries */, @@ -505,6 +511,10 @@ buildActionMask = 2147483647; files = ( ); + inputPaths = ( + "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}", + "$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + ); name = "[CP-User] [RNFB] Crashlytics Configuration"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -559,10 +569,13 @@ buildActionMask = 2147483647; files = ( ); + inputPaths = ( + "$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + ); name = "[CP-User] [RNFB] Core Configuration"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n _JSON_OUTPUT_BASE64=$(python -c 'import json,sys,base64;print(base64.b64encode(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"').read())['${_JSON_ROOT}'])))' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\n\n # config.admob_delay_app_measurement_init\n _ADMOB_DELAY_APP_MEASUREMENT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"admob_delay_app_measurement_init\")\n if [[ $_ADMOB_DELAY_APP_MEASUREMENT == \"true\" ]]; then\n _PLIST_ENTRY_KEYS+=(\"GADDelayAppMeasurementInit\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"YES\")\n fi\n\n # config.admob_ios_app_id\n _ADMOB_IOS_APP_ID=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"admob_ios_app_id\")\n if [[ $_ADMOB_IOS_APP_ID ]]; then\n _PLIST_ENTRY_KEYS+=(\"GADApplicationIdentifier\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_ADMOB_IOS_APP_ID\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; + shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n _JSON_OUTPUT_BASE64=$(python -c 'import json,sys,base64;print(base64.b64encode(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"').read())['${_JSON_ROOT}'])))' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; }; D91BC49ED9E5B6C37CAABCC6 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; @@ -742,6 +755,7 @@ files = ( 00E356F31AD99517003FC87E /* ExpensifyCashTests.m in Sources */, 0F5E5351263B73FD004CA14F /* EnvironmentChecker.m in Sources */, + 7041848626A8E47D00E09F4D /* RCTStartupTimer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -752,6 +766,7 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 18D050E0262400AF000D658B /* BridgingFile.swift in Sources */, 0F5E5350263B73FD004CA14F /* EnvironmentChecker.m in Sources */, + 7041848526A8E47D00E09F4D /* RCTStartupTimer.m in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/ExpensifyCash/AppDelegate.m b/ios/ExpensifyCash/AppDelegate.m index 5126598e3e3b..6bc463b79e9e 100644 --- a/ios/ExpensifyCash/AppDelegate.m +++ b/ios/ExpensifyCash/AppDelegate.m @@ -5,6 +5,7 @@ #import #import #import +#import "RCTStartupTimer.h" #import "RNBootSplash.h" @@ -74,6 +75,10 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [RNBootSplash initWithStoryboard:@"BootSplash" rootView:rootView]; // <- initialization using the storyboard file name + // Start the "js_load" custom performance tracing metric. This timer is stopped by a native + // module in the JS so we can measure total time starting in the native layer and ending in + // the JS layer. + [RCTStartupTimer start]; return YES; } diff --git a/ios/ExpensifyCash/RCTStartupTimer.h b/ios/ExpensifyCash/RCTStartupTimer.h new file mode 100644 index 000000000000..171aac44d4d6 --- /dev/null +++ b/ios/ExpensifyCash/RCTStartupTimer.h @@ -0,0 +1,19 @@ +// +// RCTStartupTimer.h +// ExpensifyCash +// +// Created by Marc Glasser on 7/21/21. +// +#import +#import + +#ifndef RCTStartupTimer_h +#define RCTStartupTimer_h + + +#endif /* RCTStartupTimer_h */ + +@interface RCTStartupTimer : NSObject ++ (void)start; + +@end diff --git a/ios/ExpensifyCash/RCTStartupTimer.m b/ios/ExpensifyCash/RCTStartupTimer.m new file mode 100644 index 000000000000..007e32571a49 --- /dev/null +++ b/ios/ExpensifyCash/RCTStartupTimer.m @@ -0,0 +1,33 @@ +// +// RCTStartupTimer.m +// ExpensifyCash +// +// Created by Marc Glasser on 7/21/21. +// + +#import "RCTStartupTimer.h" + +@implementation RCTStartupTimer + +static FIRTrace *trace = nil; + ++ (void)start { + #if DEBUG + // We don't want to record this on debug since it will skew the metrics we collect + NSLog(@"[StartupTimer] Metric tracing disabled in DEBUG"); + #else + trace = [FIRPerformance startTraceWithName:@"js_loaded"]; + #endif +} + +RCT_EXPORT_METHOD(stop) +{ + if (trace) { + [trace stop]; + } +} + +// To export a module named StartupTimer +RCT_EXPORT_MODULE(StartupTimer); + +@end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b0652b4a8517..3f4e4773ee98 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -24,45 +24,75 @@ PODS: - React-Core (= 0.64.1) - React-jsi (= 0.64.1) - ReactCommon/turbomodule/core (= 0.64.1) - - Firebase/Analytics (6.34.0): + - Firebase/Analytics (8.4.0): - Firebase/Core - - Firebase/Core (6.34.0): + - Firebase/Core (8.4.0): - Firebase/CoreOnly - - FirebaseAnalytics (= 6.9.0) - - Firebase/CoreOnly (6.34.0): - - FirebaseCore (= 6.10.4) - - Firebase/Crashlytics (6.34.0): + - FirebaseAnalytics (~> 8.4.0) + - Firebase/CoreOnly (8.4.0): + - FirebaseCore (= 8.4.0) + - Firebase/Crashlytics (8.4.0): - Firebase/CoreOnly - - FirebaseCrashlytics (~> 4.6.2) - - FirebaseAnalytics (6.9.0): - - FirebaseCore (~> 6.10) - - FirebaseInstallations (~> 1.7) - - GoogleAppMeasurement (= 6.9.0) - - GoogleUtilities/AppDelegateSwizzler (~> 6.7) - - GoogleUtilities/MethodSwizzler (~> 6.7) - - GoogleUtilities/Network (~> 6.7) - - "GoogleUtilities/NSData+zlib (~> 6.7)" - - nanopb (~> 1.30906.0) - - FirebaseCore (6.10.4): - - FirebaseCoreDiagnostics (~> 1.6) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Logger (~> 6.7) - - FirebaseCoreDiagnostics (1.7.0): - - GoogleDataTransport (~> 7.4) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Logger (~> 6.7) - - nanopb (~> 1.30906.0) - - FirebaseCrashlytics (4.6.2): - - FirebaseCore (~> 6.10) - - FirebaseInstallations (~> 1.6) - - GoogleDataTransport (~> 7.2) - - nanopb (~> 1.30906.0) - - PromisesObjC (~> 1.2) - - FirebaseInstallations (1.7.0): - - FirebaseCore (~> 6.10) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/UserDefaults (~> 6.7) - - PromisesObjC (~> 1.2) + - FirebaseCrashlytics (~> 8.4.0) + - Firebase/Performance (8.4.0): + - Firebase/CoreOnly + - FirebasePerformance (~> 8.4.0) + - FirebaseABTesting (8.4.0): + - FirebaseCore (~> 8.0) + - FirebaseAnalytics (8.4.0): + - FirebaseAnalytics/AdIdSupport (= 8.4.0) + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.4) + - GoogleUtilities/MethodSwizzler (~> 7.4) + - GoogleUtilities/Network (~> 7.4) + - "GoogleUtilities/NSData+zlib (~> 7.4)" + - nanopb (~> 2.30908.0) + - FirebaseAnalytics/AdIdSupport (8.4.0): + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleAppMeasurement (= 8.4.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.4) + - GoogleUtilities/MethodSwizzler (~> 7.4) + - GoogleUtilities/Network (~> 7.4) + - "GoogleUtilities/NSData+zlib (~> 7.4)" + - nanopb (~> 2.30908.0) + - FirebaseCore (8.4.0): + - FirebaseCoreDiagnostics (~> 8.0) + - GoogleUtilities/Environment (~> 7.4) + - GoogleUtilities/Logger (~> 7.4) + - FirebaseCoreDiagnostics (8.4.0): + - GoogleDataTransport (~> 9.0) + - GoogleUtilities/Environment (~> 7.4) + - GoogleUtilities/Logger (~> 7.4) + - nanopb (~> 2.30908.0) + - FirebaseCrashlytics (8.4.0): + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleDataTransport (~> 9.0) + - GoogleUtilities/Environment (~> 7.4) + - nanopb (~> 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - FirebaseInstallations (8.4.0): + - FirebaseCore (~> 8.0) + - GoogleUtilities/Environment (~> 7.4) + - GoogleUtilities/UserDefaults (~> 7.4) + - PromisesObjC (< 3.0, >= 1.2) + - FirebasePerformance (8.4.0): + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - FirebaseRemoteConfig (~> 8.0) + - GoogleDataTransport (~> 9.0) + - GoogleUtilities/Environment (~> 7.4) + - GoogleUtilities/ISASwizzler (~> 7.4) + - GoogleUtilities/MethodSwizzler (~> 7.4) + - Protobuf (~> 3.15) + - FirebaseRemoteConfig (8.4.0): + - FirebaseABTesting (~> 8.0) + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleUtilities/Environment (~> 7.4) + - "GoogleUtilities/NSData+zlib (~> 7.4)" - Flipper (0.75.1): - Flipper-Folly (~> 2.5) - Flipper-RSocket (~> 1.3) @@ -110,39 +140,49 @@ PODS: - FlipperKit/Core - FlipperKit/FlipperKitNetworkPlugin - glog (0.3.5) - - GoogleAppMeasurement (6.9.0): - - GoogleUtilities/AppDelegateSwizzler (~> 6.7) - - GoogleUtilities/MethodSwizzler (~> 6.7) - - GoogleUtilities/Network (~> 6.7) - - "GoogleUtilities/NSData+zlib (~> 6.7)" - - nanopb (~> 1.30906.0) - - GoogleDataTransport (7.5.1): - - nanopb (~> 1.30906.0) - - GoogleUtilities/AppDelegateSwizzler (6.7.2): + - GoogleAppMeasurement (8.4.0): + - GoogleAppMeasurement/AdIdSupport (= 8.4.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.4) + - GoogleUtilities/MethodSwizzler (~> 7.4) + - GoogleUtilities/Network (~> 7.4) + - "GoogleUtilities/NSData+zlib (~> 7.4)" + - nanopb (~> 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (8.4.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.4) + - GoogleUtilities/MethodSwizzler (~> 7.4) + - GoogleUtilities/Network (~> 7.4) + - "GoogleUtilities/NSData+zlib (~> 7.4)" + - nanopb (~> 2.30908.0) + - GoogleDataTransport (9.1.0): + - GoogleUtilities/Environment (~> 7.2) + - nanopb (~> 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/AppDelegateSwizzler (7.5.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (6.7.2): - - PromisesObjC (~> 1.2) - - GoogleUtilities/Logger (6.7.2): + - GoogleUtilities/Environment (7.5.0): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/ISASwizzler (7.5.0) + - GoogleUtilities/Logger (7.5.0): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.7.2): + - GoogleUtilities/MethodSwizzler (7.5.0): - GoogleUtilities/Logger - - GoogleUtilities/Network (6.7.2): + - GoogleUtilities/Network (7.5.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.7.2)" - - GoogleUtilities/Reachability (6.7.2): + - "GoogleUtilities/NSData+zlib (7.5.0)" + - GoogleUtilities/Reachability (7.5.0): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.7.2): + - GoogleUtilities/UserDefaults (7.5.0): - GoogleUtilities/Logger - libevent (2.1.12) - - nanopb (1.30906.0): - - nanopb/decode (= 1.30906.0) - - nanopb/encode (= 1.30906.0) - - nanopb/decode (1.30906.0) - - nanopb/encode (1.30906.0) + - nanopb (2.30908.0): + - nanopb/decode (= 2.30908.0) + - nanopb/encode (= 2.30908.0) + - nanopb/decode (2.30908.0) + - nanopb/encode (2.30908.0) - Onfido (20.1.0) - onfido-react-native-sdk (1.3.3): - Onfido (= 20.1.0) @@ -155,7 +195,8 @@ PODS: - Permission-LocationWhenInUse (3.0.1): - RNPermissions - Plaid (2.1.2) - - PromisesObjC (1.2.12) + - PromisesObjC (2.0.0) + - Protobuf (3.17.0) - RCT-Folly (2020.01.13.00): - boost-for-react-native - DoubleConversion @@ -446,22 +487,26 @@ PODS: - React-Core - RNCPicker (1.9.11): - React-Core - - RNFBAnalytics (7.6.8): - - Firebase/Analytics (~> 6.34.0) + - RNFBAnalytics (12.3.0): + - Firebase/Analytics (= 8.4.0) - React-Core - RNFBApp - - RNFBApp (8.4.6): - - Firebase/CoreOnly (~> 6.34.0) + - RNFBApp (12.3.0): + - Firebase/CoreOnly (= 8.4.0) + - React-Core + - RNFBCrashlytics (12.3.0): + - Firebase/Crashlytics (= 8.4.0) - React-Core - - RNFBCrashlytics (8.4.10): - - Firebase/Crashlytics (~> 6.34.0) + - RNFBApp + - RNFBPerf (12.3.0): + - Firebase/Performance (= 8.4.0) - React-Core - RNFBApp - RNGestureHandler (1.9.0): - React-Core - RNPermissions (3.0.1): - React-Core - - RNReanimated (2.1.0): + - RNReanimated (2.3.0-alpha.1): - DoubleConversion - FBLazyVector - FBReactNativeSpec @@ -596,6 +641,7 @@ DEPENDENCIES: - "RNFBAnalytics (from `../node_modules/@react-native-firebase/analytics`)" - "RNFBApp (from `../node_modules/@react-native-firebase/app`)" - "RNFBCrashlytics (from `../node_modules/@react-native-firebase/crashlytics`)" + - "RNFBPerf (from `../node_modules/@react-native-firebase/perf`)" - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNPermissions (from `../node_modules/react-native-permissions`) - RNReanimated (from `../node_modules/react-native-reanimated`) @@ -623,11 +669,14 @@ SPEC REPOS: - boost-for-react-native - CocoaAsyncSocket - Firebase + - FirebaseABTesting - FirebaseAnalytics - FirebaseCore - FirebaseCoreDiagnostics - FirebaseCrashlytics - FirebaseInstallations + - FirebasePerformance + - FirebaseRemoteConfig - Flipper - Flipper-DoubleConversion - Flipper-Folly @@ -644,6 +693,7 @@ SPEC REPOS: - OpenSSL-Universal - Plaid - PromisesObjC + - Protobuf - YogaKit EXTERNAL SOURCES: @@ -747,6 +797,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-firebase/app" RNFBCrashlytics: :path: "../node_modules/@react-native-firebase/crashlytics" + RNFBPerf: + :path: "../node_modules/@react-native-firebase/perf" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNPermissions: @@ -795,13 +847,16 @@ SPEC CHECKSUMS: DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de EXHaptics: 337c160c148baa6f0e7166249f368965906e346b FBLazyVector: 7b423f9e248eae65987838148c36eec1dbfe0b53 - FBReactNativeSpec: d65967936e86fe0fe6cca5c0125c237636868d4a - Firebase: c23a36d9e4cdf7877dfcba8dd0c58add66358999 - FirebaseAnalytics: 3bb096873ee0d7fa4b6c70f5e9166b6da413cc7f - FirebaseCore: d3a978a3cfa3240bf7e4ba7d137fdf5b22b628ec - FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 - FirebaseCrashlytics: 1a747c9cc084a24dc6d9511c991db1cd078154eb - FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2 + FBReactNativeSpec: 884d4cc2b011759361797a4035c47e10099393b5 + Firebase: 54cdc8bc9c9b3de54f43dab86e62f5a76b47034f + FirebaseABTesting: 4cb61aeeb50f60680af1c01fff781dfaf9293916 + FirebaseAnalytics: 4751d6a49598a2b58da678cc07df696bcd809ab9 + FirebaseCore: 31f389c37ac1ea52454a53d3081f2d7019485a4a + FirebaseCoreDiagnostics: cad03be1904b975f845e632f2720c3337da27faf + FirebaseCrashlytics: c9eb562b2f6bd5ee5e880144fd5ef1bfe46c5dc5 + FirebaseInstallations: 1585729afc787877763208c2088ed84221161f77 + FirebasePerformance: b6a1fd3ca81f5d54c4216e43562aa9dcd4f54b19 + FirebaseRemoteConfig: 0b813f093033c56fe74a91996044a239ab5acb02 Flipper: d3da1aa199aad94455ae725e9f3aa43f3ec17021 Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 Flipper-Folly: 755929a4f851b2fb2c347d533a23f191b008554c @@ -810,11 +865,11 @@ SPEC CHECKSUMS: Flipper-RSocket: 127954abe8b162fcaf68d2134d34dc2bd7076154 FlipperKit: 8a20b5c5fcf9436cac58551dc049867247f64b00 glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62 - GoogleAppMeasurement: a6a3a066369828db64eda428cb2856dc1cdc7c4e - GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 - GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 + GoogleAppMeasurement: 6b6a08fd9c71f4dbc89e0e812acca81d797aa342 + GoogleDataTransport: 85fd18ff3019bb85d3f2c551d04c481dedf71fc9 + GoogleUtilities: eea970f4a389963963bffe8d8fabe43540678b9c libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 - nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc + nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 Onfido: 116a268e4cb8b767c15285e8071c2e8304673cdf onfido-react-native-sdk: b8f1b7cbe1adab6479d735275772390161630dcd OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b @@ -822,7 +877,8 @@ SPEC CHECKSUMS: Permission-LocationAlways: 7f7f373d086af7a81b2f4f20d65d29266ca2043b Permission-LocationWhenInUse: 3ae82a9feb5da4e94e386dba17c7dd3531af9feb Plaid: c02276ccc630a726a9ed790bf923d29839ff4017 - PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 + PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 + Protobuf: 7327d4444215b5f18e560a97f879ff5503c4581c RCT-Folly: ec7a233ccc97cc556cf7237f0db1ff65b986f27c RCTRequired: ec2ebc96b7bfba3ca5c32740f5a0c6a014a274d2 RCTTypeSafety: 22567f31e67c3e088c7ac23ea46ab6d4779c0ea5 @@ -861,12 +917,13 @@ SPEC CHECKSUMS: RNCClipboard: 5e299c6df8e0c98f3d7416b86ae563d3a9f768a3 RNCMaskedView: 138134c4d8a9421b4f2bf39055a79aa05c2d47b1 RNCPicker: 6780c753e9e674065db90d9c965920516402579d - RNFBAnalytics: 2dc4dd9e2445faffca041b10447a23a71dcdabf8 - RNFBApp: 7eacc7da7ab19f96c05e434017d44a9f09410da8 - RNFBCrashlytics: 4870c14cf8833053b6b5648911abefe1923854d2 + RNFBAnalytics: 8ba84c2d31c64374d054c8621b998f25145ffddc + RNFBApp: 64c90ab78b6010ed5c3ade026dfe5ff6442c21fd + RNFBCrashlytics: 1de18b8cc36d9bcf86407c4a354399228cc84a61 + RNFBPerf: e3a7269f573a4787810a32de51647cdcbe08dfb4 RNGestureHandler: 9b7e605a741412e20e13c512738a31bd1611759b RNPermissions: eb94f9fdc0a8ecd02fcce0676d56ffb1395d41e1 - RNReanimated: b8c8004b43446e3c2709fe64b2b41072f87428ad + RNReanimated: 833ebd229b31e18a8933ebd0cd744a0f47d88c42 RNScreens: e8e8dd0588b5da0ab57dcca76ab9b2d8987757e0 RNSVG: ce9d996113475209013317e48b05c21ee988d42e UMAppLoader: aae896b81e3fcaa6528992e2e19ec8db38c2cedd diff --git a/package-lock.json b/package-lock.json index e0c96fb08c44..5436ff2b5e89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7856,27 +7856,32 @@ "integrity": "sha512-CW7eOhxduIxA723aZlKMOnBEz1o5Cjo5ibMNsf81TcjNqtsamRHm8jFS98za7t5P7XdhM0MhzkbaS90cwOlN+Q==" }, "@react-native-firebase/analytics": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@react-native-firebase/analytics/-/analytics-7.6.8.tgz", - "integrity": "sha512-gm4xoJva6lfPCQVprbymm6TY6FwdRsLUwVZt9GM0XY+KrvJV3yk6vhlCafvpRZCEhvVQmW8QDCf0p9VaIygv9g==" + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@react-native-firebase/analytics/-/analytics-12.3.0.tgz", + "integrity": "sha512-0+ea3ebMLwxk/qnp41mo7SQ3NbPAbQnytmqXhh0XmZW9uXCsfmYdGiUKpdswWe0jq7hCKIQtKvxbClA+Bfhcmg==" }, "@react-native-firebase/app": { - "version": "8.4.6", - "resolved": "https://registry.npmjs.org/@react-native-firebase/app/-/app-8.4.6.tgz", - "integrity": "sha512-4eZR133QuScvGs4IQQd6qMvQ5E1mtydVTy3DtuBCeiIqtvN8ZxBYoSMgcUmYGS4VafbS2nPh85g4e3BlaIJ2XA==", + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@react-native-firebase/app/-/app-12.3.0.tgz", + "integrity": "sha512-8jF7PXtvAA59YN9Hqf7mqMV4kvmEXO1XrGc8fRuOQ08g6OKEYZPDBvDsiLVy3oLw911Vznu2dllwQVm+K7Wcgw==", "requires": { "opencollective-postinstall": "^2.0.1", "superstruct": "^0.6.2" } }, "@react-native-firebase/crashlytics": { - "version": "8.4.10", - "resolved": "https://registry.npmjs.org/@react-native-firebase/crashlytics/-/crashlytics-8.4.10.tgz", - "integrity": "sha512-nrRHkMvaz/sgQL56hZLMoIY15L6HAjJmnCe134Vw3IPn95OKHTuF4iSa17uV/ezvOIJNSngHHZHFnySBRsu+Jg==", + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@react-native-firebase/crashlytics/-/crashlytics-12.3.0.tgz", + "integrity": "sha512-XECsCS01PST+UuDoS5bJrh7oUSfHCbb09YCGOeHt7tvd2hZ0Go/UenUZlvf1Uxeu1rVj7oL6lpiLHG2J9+dmiw==", "requires": { "stacktrace-js": "^2.0.0" } }, + "@react-native-firebase/perf": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@react-native-firebase/perf/-/perf-12.3.0.tgz", + "integrity": "sha512-9AA2xrx+6122IuahhA2P3CRddr0ObuTkWnLxKhWg1DXq9cHG/DOA3kB/j7HqPnOQgBoR+zYeLrJUf3+vceK9sQ==" + }, "@react-native-masked-view/masked-view": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@react-native-masked-view/masked-view/-/masked-view-0.2.4.tgz", diff --git a/package.json b/package.json index d2e5b39b5f76..ec2d90e8238b 100644 --- a/package.json +++ b/package.json @@ -44,9 +44,10 @@ "@react-native-community/netinfo": "^5.9.10", "@react-native-community/progress-bar-android": "^1.0.4", "@react-native-community/progress-view": "^1.2.3", - "@react-native-firebase/analytics": "^7.6.7", - "@react-native-firebase/app": "^8.4.5", - "@react-native-firebase/crashlytics": "^8.4.9", + "@react-native-firebase/analytics": "^12.3.0", + "@react-native-firebase/app": "^12.3.0", + "@react-native-firebase/crashlytics": "^12.3.0", + "@react-native-firebase/perf": "^12.3.0", "@react-native-masked-view/masked-view": "^0.2.4", "@react-native-picker/picker": "^1.9.11", "@react-navigation/compat": "^5.3.15", diff --git a/src/Expensify.js b/src/Expensify.js index 421658fb55ee..750efc617062 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -20,6 +20,7 @@ import GrowlNotification from './components/GrowlNotification'; import {growlRef} from './libs/Growl'; import Navigation from './libs/Navigation/Navigation'; import ROUTES from './ROUTES'; +import StartupTimer from './libs/StartupTimer'; // Initialize the store when the app loads for the first time Onyx.init({ @@ -94,6 +95,10 @@ class Expensify extends PureComponent { } componentDidMount() { + // This timer is set in the native layer when launching the app and we stop it here so we can measure how long + // it took for the main app itself to load. + StartupTimer.stop(); + // Run any Onyx schema migrations and then continue loading the main app migrateOnyx() .then(() => { diff --git a/src/libs/StartupTimer/index.js b/src/libs/StartupTimer/index.js new file mode 100644 index 000000000000..7b53266e50d9 --- /dev/null +++ b/src/libs/StartupTimer/index.js @@ -0,0 +1,7 @@ +/** + * Web noop as there is no "startup" to time from the native layer. + */ + +export default { + stop() {}, +}; diff --git a/src/libs/StartupTimer/index.native.js b/src/libs/StartupTimer/index.native.js new file mode 100644 index 000000000000..29bbef8cfe3d --- /dev/null +++ b/src/libs/StartupTimer/index.native.js @@ -0,0 +1,12 @@ +import {NativeModules} from 'react-native'; + +/** + * Stop the startup trace for the app. + */ +function stop() { + NativeModules.StartupTimer.stop(); +} + +export default { + stop, +};