appandflow / appandflow/react-native-safe-area-context

SafeAreaProvider renders nothing after an iOS background launch (initialWindowMetrics null and onInsetsChange never arrives)

Offen
#774 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
Vorherrschende Sprache
TypeScript
Sterne
2.8k
Forks
258
Ø Merge
3 T. 1 Std.
Gemergte PRs (30 T.)
2

Beschreibung

## Summary

On iOS with the new architecture, when the OS launches the app **in the background** (CoreBluetooth state restoration, a CoreLocation region entry), `initialWindowMetrics` is `null` and `SafeAreaProvider` then depends entirely on one `onInsetsChange` event from `RNCSafeAreaProviderComponentView`. In that launch path the event is mostly lost, so `SafeAreaProvider` keeps `insets === null` and renders **no children**. When the user later opens the app they get a bare window (black in a dark theme, white in a light one) until they force quit. We had riders of a production app reporting exactly this as a "black screen when reopening the app".

## Why `initialWindowMetrics` is null

`RNCSafeAreaContext.getConstants` reads `RCTKeyWindow()`. React Native's `RCTKeyWindow` only returns a window from a scene in `foregroundActive` or `foregroundInactive` state. A background launch has neither, so the constants carry `initialWindowMetrics: null`. That part is expected; the problem is what happens next.

## Why the event is lost

`RNCSafeAreaProviderComponentView.invalidateSafeAreaInsets` sends once per view (`_initialInsetsSent`) and afterwards only when the insets or frame change. In a background launch the first layout passes happen while the app is not on screen, and by the time the app comes to the foreground the view's insets and frame no longer change, so no further event is sent. A recycled view whose frame does not change on re-use has the same problem for a remounted JS `SafeAreaProvider`, which starts from `insets === null` again.

## Reproduction (iOS 26.4 simulator, deterministic enough)

1. App with `SafeAreaProvider initialMetrics={initialWindowMetrics}` at the root and the `location` background mode, monitoring a `CLCircularRegion`.
2. `xcrun simctl location set` a point 15 km outside the region, `xcrun simctl terminate` the app, then set the location inside the region. iOS relaunches the app in the background within about 20 s.
3. `xcrun simctl launch` the app to bring it to the foreground.

Result on our app: blank window in 4 of 5 cycles. Inspecting the React tree over the Hermes inspector shows `SafeAreaProvider` with `insets === null` and no child fibers. Rotating the simulator forces a layout pass and the UI appears immediately, which confirms the provider was only waiting for the event.

## Environment

- react-native-safe-area-context 5.9.1
- react-native 0.86.3, new architecture (Fabric), Hermes
- iOS 26.4 simulator and iOS 26.6.1 devices

## Proposed fix (what we ship as a patch-package)

Make sure every JS instance hears from the native view at least once, regardless of whether a layout pass would otherwise happen:

```objectivec
// RNCSafeAreaProviderComponentView.mm
- (void)updateEventEmitter:(const facebook::react::EventEmitter::Shared &)eventEmitter
{
[super updateEventEmitter:eventEmitter];
// A new JS instance (remount, or a recycled native view) must get its own send.
_initialInsetsSent = NO;
}

- (void)didMoveToWindow
{
[super didMoveToWindow];
// Ask for a layout pass rather than sending here: safeAreaInsets are only
// valid once the view has been laid out in the window, and a frame that did
// not change would otherwise get no layout pass at all.
if (self.window != nil) {
[self setNeedsLayout];
}
}
```

With this patch the same 5 relaunch cycles rendered 5 of 5, and a normal launch still produces the same small number of `onInsetsChange` sends (no extra traffic on background and foreground transitions).

On the JS side we also stopped passing `null` as `initialMetrics`: we persist the last insets the provider settled on and pass those (on the window frame from `Dimensions`) when `initialWindowMetrics` is null, so the tree renders immediately and the native report corrects it. It may be worth documenting that `initialWindowMetrics` is null on background launches, since the README presents it as a way to render on the first frame.

Happy to open a PR with the native change if you would take it.

Beitragsleitfaden

Beitragsleitfaden öffnen

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.