Skip to content

feat: add perf metrics - #494

Merged
hurali97 merged 17 commits into
mainfrom
hur/feat/perf-metrics
Oct 16, 2026
Merged

hurali97 merged 17 commits into
mainfrom
hur/feat/perf-metrics

Conversation

@hurali97

Copy link
Copy Markdown
Member

Summary

This PR introduces performance metrics which can be used to track for observability OR custom usage. The supported metrics are:

JS Bundle:

  • Load Time
  • Execution Time

Screen Based:

  • TTI
  • TTFD
  • JS FPS
  • UI FPS

Test plan

  • CI Passes
  • Verified Locally

hurali97 and others added 9 commits September 7, 2026 16:32
… TTFD hook (#493)

* fix(bgp): track RN bundle dirs as merge task inputs

Static source directories added through the variant API are not task
inputs, so a rebuilt JS bundle left mergeAssets/packageResources
UP-TO-DATE and the AAR shipped the previous bundle. Register the
generated assets/res dirs (including the expo-updates resources) as
inputs of those tasks.

* fix(Expo): respect safe-area insets on the postMessage tab

Pad the postMessage tab with the safe-area insets (and the bottom tab
inset) so its content is not drawn under the status bar or tab bar when
the Expo surface is hosted full screen. ExpoApp56's Jest setup mocks
react-native-safe-area-context, since the tests render the tab without a
SafeAreaProvider.

* feat(AndroidApp): host React Native in a full-screen ReactNativeActivity

Move the embedded RN fragment out of MainActivity's scroll view into a
dedicated ReactNativeActivity pushed from an "Open React Native" button,
mirroring RNAppScreen on iOS. Each launch creates a new fragment, so
every presentation reports its own TTID/TTFD. The activity now owns the
BrownfieldNavigation delegate.

Detox suites open the RN screen through the new button instead of
scrolling the native shell, and window-focus recovery no longer brings
MainActivity to the front while another app activity is resumed.

* test(AppleApp): open RN via accessibility id, keep postMessage toast on top

Give the "Open React Native" button an accessibility identifier so the
Detox suites tap it directly instead of matching by text, and move the
postMessage toast above the NavigationView so it stays visible while the
pushed RN screen is in front.

* feat(examples): mark TTFD from every example app via a shared hook

Add a useMarkFullyDisplayed hook to RNApp, ExpoApp56 and ExpoApp57 and
call it from each root (and the Expo Router root). It marks the
presentation fully displayed after DEMO_FULL_DISPLAY_DELAY_MS, which
stands in for the data a real screen would load first.

ExpoApp56 never called markFullyDisplayed even though its Android flavor
waits for full display, so it reported no display metrics at all.

* test(AndroidApp): tap RN-to-native navigation buttons with sync off

The vanilla settings/referrals tests tapped an RN <Button> with Detox
synchronization on. The press queues ripple view commands, and onPress
pushes a native activity straight away; once ReactNativeActivity pauses,
Fabric never mounts those commands, so FabricUIManagerIdlingResources
stays busy ("View command mount items pending: 1") and the tap times out
intermittently.

Tap with sync off and wait for the native screen through UIAutomator,
which the tests already did for the assertion.
Copilot AI balanced review requested due to automatic review settings October 15, 2026 09:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The Gradle compatibility fixture, iOS lockfile, and Expo presentation timing need correction before approval.

6 open findings

🧠 Review effort: Lite

Comment thread packages/react-native-brownfield/ios/JSBundleLoadObserver.swift
Comment thread packages/react-native-brownfield/ios/ReactNativeBrownfield.swift Outdated
Comment thread packages/react-native-brownfield/ios/ReactNativeBrownfield.swift Outdated
Comment thread docs/docs/docs/api-reference/react-native-brownfield/objective-c.mdx Outdated
Comment thread docs/docs/docs/api-reference/react-native-brownfield/swift.mdx Outdated
Add a Performance Metrics guide covering startup timings, TTID, TTFD,
JS FPS and UI FPS. Document markFullyDisplayed in the JavaScript API
reference and remove PERFORMANCE.md.
The plugin wires createBundle<Variant>JsAndAssets jsBundleDir/resourcesDir
outputs as generated sources and fails fast without them, so plain
tasks.register stubs broke the AGP compatibility matrix.
A failed reload left didLoadBundle and latestTimings from the previous
load, so a new onBundleLoaded registration got stale timings right away.
The merge duplicated the boost, BrownfieldNavigation and React-bridging
entries, which made pod install crash.
Views, view controllers and fragments created without the metrics APIs
now keep their initial props as is and create no display session, on
both iOS and Android.
With Expo Updates, view() returns a placeholder and inserts the root
later. The appearance probe sat in the placeholder, so initial display
could complete before the root existed, or when its creation failed.
The probe now goes into the real root, and the view controller reports
appearance only after that.
AppleApp preloads the JS bundle at launch, and the RN view now sits
behind the Open React Native button. DetoxSync keeps the app busy on
React Native (bundle load) until RN content appears, so re-enabling
sync on the native shell made the app never go idle and every iOS
test timed out. Sync is turned back on by openReactNative once the RN
screen is visible.
Detox's element tap injects a synthesized touch at the accessibility
activation point, which SwiftUI's NavigationLink ignores: the tap was
reported as successful, but the RN screen was never pushed. Tap the
center of the Open React Native button through XCUITest (device.tap)
instead, which delivers a real touch.
@hurali97
hurali97 merged commit d28810c into main Oct 16, 2026
17 of 19 checks passed
@hurali97
hurali97 deleted the hur/feat/perf-metrics branch October 16, 2026 05:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants