Skip to content

iOS: Manual TTID/TTFD components stop working with RCT_REMOVE_LEGACY_COMPONENT_INTEROP #7014

Closed

Description

@antonis

Problem

On iOS, <Sentry.TimeToInitialDisplay> and <Sentry.TimeToFullDisplay> silently stop working when an app builds React Native with RCT_REMOVE_LEGACY_COMPONENT_INTEROP=1.

RNSentryOnDrawReporter is a legacy RCTViewManager, so on the New Architecture it only exists through RN's legacy component interop layer. With the flag on, the component is not registered, UIManager.hasViewManagerConfig('RNSentryOnDrawReporter') returns false, and getRNSentryOnDrawReporter() falls back to a plain View. Nothing logs an error or warning.

The flag is opt-in since RN 0.88. RN states that it and RCT_REMOVE_LEGACY_MODULE_INTEROP will default to on in a future release (scripts/react_native_pods.rb).

Verification

Two Release builds of samples/react-native on RN 0.88.1, both with RN compiled from source (RCT_USE_PREBUILT_RNCORE=0), on an iPhone 16 Pro simulator. The flag was the only difference. Each run navigated through the Errors, Performance and Tracker screens.

Default RCT_REMOVE_LEGACY_COMPONENT_INTEROP=1
RNSentryOnDrawReporter registered Yes No
Tracker ui.load.initial_display manual.ui.time_to_display, 49 ms auto.ui.time_to_display, 597 ms
ui.load.full_display on any screen Present (2066 ms on Tracker) Missing
time_to_full_display measurement Present Missing

Automatic TTID from the navigation integration keeps working. With only this flag on, the RNSentry module still loaded.

Not tested

  • RN's prebuilt framework with this flag. RN warns the flag only prunes headers there, so the interop layer probably stays in.
  • RCT_REMOVE_LEGACY_MODULE_INTEROP, alone or together with the component flag. RN compiles out static module registration only when both are set, and RNSentry has no ios.modulesProvider. It may still resolve through the class-name fallback in RCTTurboModuleManager, but that needs a build to confirm.
  • Android. RNSentryOnDrawReporterManager is also a legacy view manager, but RN has no matching opt-out flag there yet, and useFabricInterop still defaults to on.

Proposed fix

Port RNSentryOnDrawReporter to a real Fabric component and register it through ios.componentProvider in codegenConfig, as RNSentryReplayMask and RNSentryReplayUnmask already are. The legacy view manager has to stay for the Old Architecture.

Separately, confirm whether RNSentry needs a modulesProvider entry.

Acceptance criteria

  • <Sentry.TimeToInitialDisplay> and <Sentry.TimeToFullDisplay> record manual spans on iOS with RCT_REMOVE_LEGACY_COMPONENT_INTEROP=1
  • Behavior is unchanged on the default New Architecture build and on the Old Architecture
  • The RNSentry module is verified with both interop removal flags on
  • Android is reviewed against the same removal path

Activity

  1. self-assigned this
    on Nov 10, 2026
  2. linear-code commented on Nov 10, 2026

    @linear-code
  3. antonis commented on Nov 11, 2026

    @antonis
    ContributorAuthor

    Closing #7015 for now. The fix works, but the problem is opt-in today, and the fix relies on React Native internals that may change before the default flips. Notes for when we revisit this:

    Current impact: low

    RCT_REMOVE_LEGACY_COMPONENT_INTEROP is opt-in. It defaults to 0 in RN 0.88.1, in 0.89.0-rc.1 and on RN main. An app is only affected when all of these are true:

    1. iOS, New Architecture, RN 0.88+ (the first version with the flag)
    2. The app sets RCT_REMOVE_LEGACY_COMPONENT_INTEROP=1 itself
    3. React Native is built from source (RCT_USE_PREBUILT_RNCORE=0). Prebuilt binaries are the default, and there the flag probably only prunes headers, so interop stays in (not verified)
    4. The app uses <Sentry.TimeToInitialDisplay> / <Sentry.TimeToFullDisplay>. Automatic TTID from the navigation integration is unaffected

    When this happens it fails silently: manual TTID/TTFD spans are missing and nothing is logged.

    Future impact: higher

    RN says both interop flags "will default to 1 in a future React Native release" (scripts/react_native_pods.rb). No version is named.

    • Module interop is the bigger risk. Testing for fix(ios): Keep manual time to display working without legacy interop #7015 showed that with both RCT_REMOVE_LEGACY_COMPONENT_INTEROP and RCT_REMOVE_LEGACY_MODULE_INTEROP set, the RNSentry native module doesn't load at all. That breaks the whole native SDK, not just time to display. A plain modulesProvider entry crashes at launch, because RNSentry doesn't declare NativeRNSentrySpec conformance. Declaring it is the likely fix.

    • What fix(ios): Keep manual time to display working without legacy interop #7015 depended on, and could break before the flip:

      • #ifdef RCT_REMOVE_LEGACY_COMPONENT_INTEROP reaching our pod. If RN removes interop together with the macro, or the flip ships through prebuilt binaries without passing the macro to third-party pods, the registration compiles out.
      • JS detection through the absence of the undocumented RN$LegacyInterop_UIManager_getConstants global.
      • The deep import of react-native/Libraries/NativeComponent/NativeComponentRegistry, now that deep imports are deprecated, and registration through RCTComponentViewFactory in +load.

      Any of these changing would bring back the same silent failure, not a crash.

    When we pick this up

    • Re-test against the RN release candidate or nightly that flips the default, with both flags on and both source and prebuilt builds.
    • Add a CI build with the flags on, so a regression doesn't go unnoticed.
    • Prefer a proper Fabric component registered through ios.componentProvider (like RNSentryReplayMask/Unmask) once interop is gone, so we don't rely on the macro or the global check.
    • Android: RN has no matching removal flag yet, and useFabricInterop still defaults to on. Review it when one appears.

    #7015 (branch fix/ios-ondraw-reporter-without-interop) is still a working starting point.

  4. removed their assignment
    on Nov 11, 2026
  5. antonis commented on Nov 11, 2026

    @antonis
    ContributorAuthor

    Closing this as an edge case for now. We can revisit this as we test and adopt future RN versions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions