Skip to content

fix(ios): skip provider inset reports while outside a window - #807

Draft
floco-af wants to merge 1 commit into
mainfrom
floco/provider-window-guard-806
Draft

floco-af wants to merge 1 commit into
mainfrom
floco/provider-window-guard-806

Conversation

@floco-af

@floco-af floco-af commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #806. cc @janicduplessis

On iOS (Fabric), RNCSafeAreaProviderComponentView listens for keyboard notifications on every instance and each one calls invalidateSafeAreaInsets.... That method checked superview and a zero frame but not window. A view outside a window reports safeAreaInsets of zero, so one keyboard event made every provider that was laid out but not in a window send top: 0 to JS. With native tab containers, the first open of each never-selected tab then painted under the status bar and jumped when the real inset arrived.

The change:

  • Return early in invalidateSafeAreaInsetsMeasuringCorners: when self.window == nil (iOS/tvOS/visionOS; macOS untouched). RNCSafeAreaViewComponentView already has this guard.
  • Add didMoveToWindow, which calls setNeedsLayout, so a provider whose in-window insets equal the zero it held outside still sends its first report (safeAreaInsetsDidChange only fires on change). It asks for layout instead of measuring directly because UIKit may not have updated safeAreaInsets at that point.

The diagnosis and patch are from @nicklamont's report in #806, who also offered to open the PR. I only validated and wrapped it.

Not changed: the legacy (Paper) ios/RNCSafeAreaProvider.m has the same unguarded path. The report is Fabric-only, so I left it out of this PR.

Test Plan

No test added: the repo has no iOS unit test target, and the behaviour depends on UIKit window/keyboard events.

Validated on an iOS 27.0 simulator (iPhone 17 Pro) in the example app (Fabric, RN 0.85), temporarily replacing the example's App with a repro that is not part of this PR: @react-navigation/bottom-tabs/unstable native tabs with lazy: false, each tab wrapped in its own SafeAreaListener (same native provider view) that records the top of every onInsetsChange, and a TextInput on tab A.

Steps: cold start on tab A, focus the TextInput (software keyboard shows), dismiss it, then select tab B and tab C for the first time.

Tab B reports Tab C reports
Before (main) [0, 116] [0, 116]
After (this PR) [116] [116]

Before, each tab's provider sent top: 0 at the keyboard event and then 116 when it entered the window. After, the provider sends nothing outside the window and one correct report on entry. Screenshots of tab B before and after are attached in the next comment.

Also run: yarn validate:typescript and yarn validate:jest pass. clang-format has no linux-arm64 binary on my machine, so the diff was formatted by hand to the repo's 120 column style; CI will check it. This repro uses react-navigation's native tabs rather than expo-router's NativeTabs from the report.

A SafeAreaProvider that is laid out but outside a window (for example a tab of a native tab container that was never selected) reports zero safeAreaInsets. A keyboard notification then sent that zero to JS, so the tab's first frame painted with top: 0 and jumped when the real inset arrived.

Skip the measurement without a window, as RNCSafeAreaViewComponentView already does, and request a layout pass when the view enters a window so the first report is always sent.

Fixes #806

Co-authored-by: Nick Lamont <39568874+nicklamont@users.noreply.github.com>
@vercel

vercel Bot commented Nov 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
react-native-safe-area-context Ignored Ignored Nov 4, 2026 4:15am UTC

Request Review

@floco-af

floco-af commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Before / after of tab B right after its first selection (keyboard shown and dismissed on tab A beforehand). First image is main: top reports: [0, 116], so a zero reached JS. Second image is this PR: top reports: [116].

806-before-tabB

806-fixed-tabB

This branch has not been deployed

No deployments
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.

[iOS, Fabric] A SafeAreaProvider outside a window sends zero insets on keyboard notifications

1 participant