chore: replace @shopify/react-native-performance with RN's built-in startup timing#7424
Draft
janicduplessis wants to merge 1 commit intodevelopfrom
Draft
chore: replace @shopify/react-native-performance with RN's built-in startup timing#7424janicduplessis wants to merge 1 commit intodevelopfrom
janicduplessis wants to merge 1 commit intodevelopfrom
Conversation
0dd50b3 to
7de3906
Compare
β¦tartup timing Drops the unmaintained `@shopify/react-native-performance` lib + its patch and sources the cold-start anchor from React Native's built-in `performance.rnStartupTiming.startTime` instead. Existing `performance.report` Amplitude event shape is preserved. Native wiring (replaces ReactNativePerformance.onAppStarted): - Android: ReactMarker.logMarker(APP_STARTUP_START) at the top of MainApplication.onCreate. Pre-bridge calls are buffered in sNativeReactMarkerQueue and replayed once JNI loads. - iOS: RCTPerformanceLogger().markStart(for: .appStartup) in application(_:didFinishLaunchingWithOptions:). markStart fires ReactMarker::logMarkerDone(APP_STARTUP_START), which writes to a static C++ singleton β no need to retain the logger instance. JS: - src/performance/start-time reads performance.rnStartupTiming.startTime (falls back to performance.now() if absent β e.g. in tests). - src/performance/tracking switches Date.now() to performance.now() so all durations and segment timings are in the same monotonic clock domain as rnStartupTiming. (steady_clock on both platforms.) - src/App.tsx drops <PerformanceProfiler> and onReportPrepared. - WelcomeScreen / WalletScreen drop their <PerformanceMeasureView> wraps. The "TTI" trigger that previously came from <PerformanceProfiler>'s render-stability heuristic now fires from useHideSplashScreen on first splash-hide β same once-only flag, fires the tti segment, finishes initialScreenInteractiveRender, and emits the appStartup report.
7de3906 to
f28802f
Compare
10 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed (plus any additional context for devs)
Drops
@shopify/react-native-performance(unmaintained since Aug 2022, has an open Kotlin override-variance compile bug on RN 0.81+ that we're patching around) and replaces it with React Native's built-inperformance.rnStartupTimingAPI. Theperformance.reportandPerformance Wallet Initialize TimeAmplitude events are preserved.performance.rnStartupTiming.startTimeis the same shape of signal we get today β a process-start timestamp captured natively before the JS bundle loads β but exposed via RN's ownStartupLoggersingleton instead of a third-party module + custom patch. RN itself doesn't fire the marker by default β the host app has to fireAPP_STARTUP_STARTfrom native code, which is exactly what the Shopify lib'sReactNativePerformance.onAppStarted()was doing.Native wiring
MainApplication.kt:ReactMarker.logMarker(ReactMarkerConstants.APP_STARTUP_START)as the first line ofonCreate. Pre-bridge calls are buffered insNativeReactMarkerQueueand replayed once JNI loads (ReactMarker.java).AppDelegate.swift:RCTPerformanceLogger().markStart(for: .appStartup)fromapplication(_:didFinishLaunchingWithOptions:).markStartfiresReactMarker::logMarkerDone(APP_STARTUP_START)which writes to a static C++ singleton, so the throwaway logger doesn't need to be retained. The bridging header gains<React/RCTPLTag.h>and<React/RCTPerformanceLogger.h>so Swift can see the API.JS
src/performance/start-time/index.tsreadsperformance.rnStartupTiming.startTime, falling back toperformance.now()when absent (e.g. in jest, where the TurboModule isn't wired).src/performance/tracking/index.tsswitchesDate.now()βperformance.now()everywhere reports anchor time. With the Shopify lib we were mixing wall-clock (Date.now()for segment math) and mach time (nativeStartupTimestampfrom the lib's iOS captures) β that's a latent bug the new wiring fixes incidentally, sincernStartupTiming.startTimeandperformance.now()are bothsteady_clockon both platforms.src/App.tsxdrops<PerformanceProfiler>and theonReportPreparedcallback.WelcomeScreenandWalletScreendrop their<PerformanceMeasureView>wraps.Where the "TTI" signal comes from now
The Shopify lib's
<PerformanceProfiler>had a render-stability heuristic to detect "the screen has settled" and fire itsonReportPreparedcallback β that's where thettisegment ofperformance.reportwas logged. The replacement signal is splash-hide:useHideSplashScreen.tsalready gates a once-only block on first hide; that's where we now log thettisegment, finishinitialScreenInteractiveRender, and emitappStartup.This is slightly earlier than the prior heuristic on
WalletScreen(where<PerformanceMeasureView interactive={!isLoadingUserAssets}>waited for assets to load before reporting interactive). For an existing wallet, expect reported TTI to drop by the asset-load delta β closer to "first paint" than "fully populated screen". This was a deliberate trade-off: simpler signal, no third-party heuristic, easy to reason about. If we want the asset-load gating back later, that's one extrauseEffectin the screen.Build
@shopify/react-native-performanceremoved frompackage.json+yarn.lock.patches/@shopify+react-native-performance+4.1.2.patchdeleted.ios/Podfile.lockregenerated. DropsReactNativePerformance. Side-effect: picks up anRNFlashList 1.8.2 β 1.8.3andreact-native-udpchecksum drift that were already inconsistent between develop'spackage.jsonandPodfile.lockβ pod install resolved the mismatch on the way through.Screen recordings / screenshots
N/A β no visual changes.
What to test
performance.reportarrives in Amplitude.performance.reportarrives in Amplitude. (Expect TTI value to be slightly lower than baseline β see "Where the TTI signal comes from now" above.)yarn android) βReactMarker.logMarker(APP_STARTUP_START)is the load-bearing change here.bundle exec pod installsucceeds (dropsreact-native-performance),yarn iossucceeds. Confirm the bridging-header import resolves (<React/RCTPLTag.h>,<React/RCTPerformanceLogger.h>).performance.reportevents still arrive,ttisegment is non-zero and roughly tracks baseline (allowing for the<PerformanceMeasureView>semantic change above).