react-native-screens: Fabric UIViewControllerHierarchyInconsistency A view can only be associated with at most one view controller at a time! View

getting this error only when I enable the fabric

name __NSCFConstantString * “UIViewControllerHierarchyInconsistency” 0x000000020be3fdf0

Exception NSException * “A view can only be associated with at most one view controller at a time! View <RNSScreenView: 0x113814000; frame = (0 0; 0 0); layer = <CALayer: 0x2812ead00>> is associated with <RNSScreen: 0x111e15f20>. Clear this association before associating this view with <RNSScreen: 0x11d6873f0>.” 0x0000000281d4e4f0

here is my code

const TestComponent = () => {
  return <Text>TestComponent</Text>;
};
const App = () => {
  return (
    <NavigationContainer>
      <RootStack.Navigator
        screenOptions={{
          headerHideBackButton: true,
        }}
      >
        <RootStack.Screen
          name="Chapter"
          options={{
            title: "Fabric Example",
            headerShown: false,
          }}
          initialParams={{
            index: 0,
            chapterRoute: "Chapter",
            afterChapterRoute: "HeaderDemo",
          }}
          component={TestComponent}
        />
        <RootStack.Screen
          name="HeaderDemo"
          component={TestComponent}
          options={{ title: "Header Demo" }}
        />
      </RootStack.Navigator>
    </NavigationContainer>
  );
};

export default App;

    "react-native": "0.72.1",
    "react-native-screens": "3.22.0",
image

here is repro: https://github.com/numandev1/UIViewControllerHierarchyInconsistency-error-repro

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 1
  • Comments: 31 (8 by maintainers)

Most upvoted comments

hi there! this is kudo from expo. thanks for the investigation and sorry to have the issue in our side.

  1. EXReactCompatibleHelpers.m i’m creating https://github.com/expo/expo/pull/24004 to fix that. in the meantime, please try to apply the patch to continue the testing.

    diff --git a/packages/expo-modules-core/ios/ReactDelegates/EXReactCompatibleHelpers.m b/packages/expo-modules-core/ios/ReactDelegates/EXReactCompatibleHelpers.m
    index 50c001939c71..82fadc3e19be 100644
    --- a/packages/expo-modules-core/ios/ReactDelegates/EXReactCompatibleHelpers.m
    +++ b/packages/expo-modules-core/ios/ReactDelegates/EXReactCompatibleHelpers.m
    @@ -4,22 +4,22 @@
     
     #import <React/RCTRootView.h>
     
    -#if __has_include(<React/RCTAppSetupUtils.h>)
    +#if __has_include(<React-RCTAppDelegate/RCTAppSetupUtils.h>)
    +#import <React-RCTAppDelegate/RCTAppSetupUtils.h>
    +#elif __has_include(<React_RCTAppDelegate/RCTAppSetupUtils.h>)
    +// for importing the header from framework, the dash will be transformed to underscore
    +#import <React_RCTAppDelegate/RCTAppSetupUtils.h>
    +#else
    +// react-native < 0.72
     #import <React/RCTAppSetupUtils.h>
     #endif
     
     UIView *EXAppSetupDefaultRootView(RCTBridge *bridge, NSString *moduleName, NSDictionary *initialProperties, BOOL fabricEnabled)
     {
    -#if __has_include(<React/RCTAppSetupUtils.h>)
    -
     #if REACT_NATIVE_MINOR_VERSION >= 71
       return RCTAppSetupDefaultRootView(bridge, moduleName, initialProperties, fabricEnabled);
     #else
       return RCTAppSetupDefaultRootView(bridge, moduleName, initialProperties);
     #endif // REACT_NATIVE_MINOR_VERSION >= 71
    -
    -#else
    -  return [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
    -#endif // __has_include(<React/RCTAppSetupUtils.h>)
     }
    
  2. react-native-gesture-handler issue not sure why the old issue comes up again. i have to enable the inlinesRequires

    diff --git a/metro.config.js b/metro.config.js
    index 0dfff20..6af3b75 100644
    --- a/metro.config.js
    +++ b/metro.config.js
    @@ -7,6 +7,15 @@ const { mergeConfig } = require('@react-native/metro-config');
      *
      * @type {import('metro-config').MetroConfig}
      */
    -const config = {};
    +const config = {
    +  transformer: {
    +    getTransformOptions: async () => ({
    +      transform: {
    +        experimentalImportSupport: false,
    +        inlineRequires: true,
    +      },
    +    }),
    +  },
    +};
    
     module.exports = mergeConfig(getDefaultConfig(__dirname), config);
    

after these modifications, i can launch the app but i did not see the A view can only be associated with at most one view controller at a time! error.

I’ve brought this up with the Expo team and they are looking at it internally as well, will keep you posted

I’ve spent some time debugging it and it seems to me that Expo is responsible for this issue. Few things I’ve managed to find up to this moment (will debug it further):

  1. In EXReactCompatibleHelpers.m ifdef statement _has_include(<React/RCTAppSetupUtils.h>) does not work as expected – it does not discover the header despite it being present in project, thus wrong application root view is created. Exchanging angle-bracket include for quote form resolves the issue, thus conclusion that some include search paths are misconfigured.
  2. After resolving issue 1., I’ve got next one: image

indicating that nativeFabricUIManager was not injected to global from native side ==> Expo most likely interferes with process of creation & setup of turbo modules.

I would recommend opening issue on Expo’s repo.

I’ll leave this issue open until I’m convinced that there is nothing to be done on react-native-screens side.

hey @kkafar - you can reproduce this issue using the next version of Ignite

set up a new project with (should be alpha5)

npx ignite-cli@next new NewArchScreens --bundle=com.newarchscreens --git --install-deps --packager=yarn --remove-demo=false --workflow=prebuild --experimental=new-arch

answer prompt on what directory you want it, cd after install and then run via yarn ios

@kkafar can you pls check ignite’s boilerplate? Facing the same issue there

Hello, I’m seeing the same error in expo 49.0.21 and React Native 0.72.7 (only on IOS in Release mode, all other modes work great!):

    › Logs for your project will appear below. Press Ctrl+C to exit.
    [<elided>] 🟡 {"code":"JSRuntimeError","message":"EXUpdates: Could not emit event: name = Expo.nativeUpdatesStateChangeEvent, type = restart. Event will be emitted when the bridge is
    available","timestamp":1704978392000,"level":"warn"}
    [<elided>] 🟡 {"code":"JSRuntimeError","message":"EXUpdates: Could not emit event: name = Expo.nativeUpdatesStateChangeEvent, type = check. Event will be emitted when the bridge is
    available","timestamp":1704978392000,"level":"warn"}
    [CoreFoundation] *** Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency', reason: 'A view can only be associated with at most one view controller at a time! View <RNSScreenView:
    0x11a8ac400; frame = (0 0; 0 0); layer = <CALayer: 0x600000297ca0>> is associated with <RNSScreen: 0x118c35e30>. Clear this association before associating this view with <RNSScreen: 0x126d0bab0>.'
    *** First throw call stack:
    (
    0   CoreFoundation                      0x0000000180437330 __exceptionPreprocess + 172
    1   libobjc.A.dylib                     0x0000000180051274 objc_exception_throw + 56
    2   CoreFoundation                      0x0000000180437240 -[NSException initWithCoder:] + 0
    3   UIKitCore                           0x0000000115f79638 -[UIView __setViewDelegate:] + 140
    4   UIKitCore                           0x00000001154450c0 -[UIViewController setView:] + 368
    5   <elided>                                0x00000001028b1f4c -[RNSScreen initWithView:] + 96
    6   <elided>                                0x000<…>
    › Stopped server
    ✨  Done in 234.44s.

I’m not sure if this is an issue with expo or react-native-screens (or something else).

Versions:

"react-native": "0.72.7",
"react-native-screens": "~3.27.0",
 "expo": "^49.0.21",

This is closed-source code right now, so any suggestions would be highly appreciated! 🙏 . I’ll create an issue on the expo repo as well and link to this.

(FWIW, I see the changes in the patch mentioned above in the expo-core-modules installed dependency).

Please ignore this now. Looks like I ran into https://github.com/expo/expo/issues/23382 and removing expo-updates fixed the crash. 🙏

@Kudo @kkafar I can confirm that after patching those two files, Fabric successfully runs without that issue now. It definitely seems related to gesture-handler as the demo Ignite code which incorporates it is acting odd when using DrawerLayout (it opens by default when the screen renders and remains stuck in the open position).

But otherwise things generally seem functional. Here is the repo: https://github.com/frankcalise/IgniteFabricIos

In addition to the expo-modules-core patch, this was the metro.config.js used:

module.exports = {
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
};

image

@kkafar I created a reproduction repo. Just call yarn install && yarn bundle-install && yarn pod-install && yarn ios . That will reproduce the problem. Thanks everyone.

Hey, I’ve just checked the repro – the build fails with missing plist file, as reported earlier:

image

@kkafar sorry, fixed now, can you check now

hey @kkafar - you can reproduce this issue using the next version of Ignite

set up a new project with (should be alpha5)

npx ignite-cli@next new NewArchScreens --bundle=com.newarchscreens --git --install-deps --packager=yarn --remove-demo=false --workflow=prebuild --experimental=new-arch

answer prompt on what directory you want it, cd after install and then run via yarn ios