Skip to content

Link error on RN 0.85 / Expo SDK 56: undefined symbol _OBJC_CLASS_$_RCTRootContentView referenced from RCTView+Helpers.swift #44

Description

@robertherber

Summary

react-native-ios-utilities@5.2.0 references RCTRootContentView in RCTView+Helpers.swift, but RN 0.85 (Expo SDK 56) builds with RCT_REMOVE_LEGACY_ARCH=1 by default — RCTRootContentView is wrapped in #ifndef RCT_REMOVE_LEGACY_ARCH in React Native, so it never gets compiled into React.framework. The link step then fails with:

Undefined symbols for architecture arm64:
  "_OBJC_CLASS_$_RCTRootContentView", referenced from:
       in libreact-native-ios-utilities.a[48](RCTView+Helpers.o)
ld: symbol(s) not found for architecture arm64

This blocks any new-arch / Expo SDK 56 app from building when react-native-ios-utilities (or downstream react-native-ios-context-menu) is in the dependency tree.

Repro

  • React Native 0.85.x with the new architecture (or Expo SDK 56, which sets RCT_REMOVE_LEGACY_ARCH=1 unconditionally during prebuild).
  • react-native-ios-utilities@5.2.0 as a dep (direct or transitive via react-native-ios-context-menu@3.2.1).
  • Build the iOS target — link step fails with the undefined symbol above.

Cause

apple/Sources/Extensions+Helpers/RCTView+Helpers.swift line 28 declares closestParentReactContentView: RCTRootContentView? and references RCTRootContentView.self. Line 55 chains through it. The property is otherwise unused anywhere in this package or in react-native-ios-context-menu, so it's effectively dead code that drags in a legacy-arch symbol.

Suggested fix

Stub the property + its single fallback caller so the symbol isn't referenced. Diff:

diff --git a/ios/Sources/Extensions+Helpers/RCTView+Helpers.swift b/ios/Sources/Extensions+Helpers/RCTView+Helpers.swift
@@ -25,16 +25,10 @@ public extension RCTView {
     return rootView.recursivelyFindSubview(whereType: targetType);
   };

-  var closestParentReactContentView: RCTRootContentView? {
-    let targetType = RCTRootContentView.self;
-
-    if let match = self.recursivelyFindParentView(whereType: targetType) {
-      return match;
-    };
-
-    guard let rootView = self.rootViewForCurrentWindow else { return nil };
-    return rootView.recursivelyFindSubview(whereType: targetType);
-  };
+  // RCTRootContentView is excluded when RCT_REMOVE_LEGACY_ARCH=1 (default in
+  // RN 0.85+ / Expo SDK 56). Property is unused by this package and downstream,
+  // so stub to nil to avoid an undefined-symbol linker error.
+  var closestParentReactContentView: AnyObject? { nil };

   var reactTouchHandlers: [RCTTouchHandler]? {
     self.gestureRecognizers?.compactMap {
@@ -58,6 +52,8 @@ public extension RCTView {
       currentView = superview;
     };

-    return closestParentReactContentView?.reactTouchHandlers?.first;
+    // Legacy `closestParentReactContentView` chain returned nil under new arch
+    // (RCTRootContentView excluded by RCT_REMOVE_LEGACY_ARCH=1); inline that.
+    return nil;
   };
 };

A more proper fix could keep the property but guard the body with #if !RCT_REMOVE_LEGACY_ARCH / use #if canImport or runtime NSClassFromString("RCTRootContentView") lookup. The stub above is the minimal-surface-area change that unblocks consumers on RN 0.85+.

Environment

  • Xcode 26.5
  • iOS Simulator 26.3 / 26.5
  • React Native 0.85.3
  • Expo SDK 56.0.11
  • react-native-ios-utilities 5.2.0
  • react-native-ios-context-menu 3.2.1 (downstream consumer)

Happy to open a PR if helpful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions