Skip to content

enforceDesiredDeviceOrientation silently fails on iOS 16+ during screen transitions #3831

@JeanDes-Code

Description

@JeanDes-Code

Description

On iOS 16+, enforceDesiredDeviceOrientation in RNSScreenWindowTraits.mm has two bugs that cause per-screen orientation changes to silently fail during navigation transitions:

  1. Stale windowScene.interfaceOrientation — The heuristic checks interfaceOrientation to decide whether rotation is needed. This value can be stale after scene restoration, app relaunch, or a previous requestGeometryUpdate that changed internal metadata without visually rotating. When stale, the method thinks the device is already in the correct orientation and skips the rotation entirely. (Same root cause as Orientation is not locked as intended #2197)

  2. Empty error handler on requestGeometryUpdate — When called during viewWillAppear, UIKit's internal orientation cache may still hold the outgoing screen's mask. requestGeometryUpdate validates against this stale cache and fails with:

    Error Domain=UISceneErrorDomain Code=101
    "None of the requested orientations are supported by the view controller.
     Requested: portrait, portraitUpsideDown; Supported: landscapeRight"
    

    The error handler is ^(NSError *_Nonnull error){} — the failure is silently swallowed.

These bugs make it unreliable to navigate between screens with different orientations (e.g. portrait home → landscape play screen → back to portrait). After 1-2 cycles the orientation gets permanently stuck.

Related: expo/expo#43692, #2197, #1775

Steps to reproduce

  1. Clone the repro: git clone https://github.com/JeanDes-Code/orientation-lock-repro
  2. npm install
  3. Delete patches/react-native-screens+4.23.0.patch (the expo-screen-orientation patch can stay — it's equivalent to expo/expo#44181)
  4. npm install again (to revert the react-native-screens patch)
  5. npx expo prebuild --clean && npx expo run:ios
  6. Tap "Go to Landscape (Option C)" — uses Stack.Screen options={{ orientation: 'landscape_right' }}
  7. Tap "Go Back" — screen should rotate to portrait but stays stuck in landscape
  8. Repeat — orientation locking fails in both directions

Snack or a link to a repository

https://github.com/JeanDes-Code/orientation-lock-repro

The repo includes a workaround patch for react-native-screens that:

  • Always calls setNeedsUpdateOfSupportedInterfaceOrientations + requestGeometryUpdate on iOS 16+ (bypasses the stale interfaceOrientation heuristic)
  • Falls back to UIDevice setValue:@(orientation) forKey:@"orientation" in the error handler
  • Moves the old heuristic to the pre-iOS 16 path only

Screens version

4.23.0

React Native version

0.83.4

Platforms

iOS

JavaScript runtime

Hermes

Workflow

Expo managed workflow

Architecture

Fabric (New Architecture)

Build type

Debug mode

Device

iOS simulator, Real device

Device model

iPhone 17 Pro Simulator (iOS 26.2), iPhone 15 Pro (iOS 18)

Acknowledgements

Yes

Metadata

Metadata

Assignees

No one assigned

    Labels

    platform:iosIssue related to iOS part of the libraryrepro-providedA reproduction with a snack or repo is provided

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions