expo: Xcode build fails Property 'reactDelegate' not found on object of type 'AppDelegate *'

Summary

After adding Expo using npx install-expo-modules@latest to an existing project, Xcode then fails to build. Android works perfectly.

The 4 x Xcode errors i am getting are: 3 x Property ‘reactDelegate’ not found on object of type ‘AppDelegate *’ 1 x No visible @interface for ‘EXAppDelegateWrapper’ declares the selector ‘application:didFinishLaunchingWithOptions:’

I have created a new AwesomeProject and then added installed expo in the same way and this works as expected so i cannot reproduce this error.

After researching, it would suggest that i am using an expo SDK 43 or below, however SDK47 is installed.

I have copied the AppDelegate.h and AppDelegate.mm from the react-native repo to simulate before expo was installed and this boots. From this i have then run the above install command and still the same issue, i cant find anyone anywhere with a similar issue.

If anyone could shed any light on this that would be great! Thanks!

AppDelegate.h

#import <React/RCTBridgeDelegate.h>
#import <Expo/Expo.h>
#import <UIKit/UIKit.h>

@interface AppDelegate : EXAppDelegateWrapper <UIApplicationDelegate, RCTBridgeDelegate>

@property (nonatomic, strong) UIWindow *window;

@end

AppDelegate.mm

#import "AppDelegate.h"

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

#import <React/RCTAppSetupUtils.h>

#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>

#import <react/config/ReactNativeConfig.h>

static NSString *const kRNConcurrentRoot = @"concurrentRoot";

@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
  RCTTurboModuleManager *_turboModuleManager;
  RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
  std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
  facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  RCTAppSetupPrepareApp(application);

  RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];

#if RCT_NEW_ARCH_ENABLED
  _contextContainer = std::make_shared<facebook::react::ContextContainer const>();
  _reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
  _contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
  _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
  bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif

  NSDictionary *initProps = [self prepareInitialProps];
  UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"mobilityapp" initialProperties:initProps];

  if (@available(iOS 13.0, *)) {
    rootView.backgroundColor = [UIColor systemBackgroundColor];
  } else {
    rootView.backgroundColor = [UIColor whiteColor];
  }

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [self.reactDelegate createRootViewController];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  [super application:application didFinishLaunchingWithOptions:launchOptions];
  return YES;
}

/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
  // Switch this bool to turn on and off the concurrent root
  return true;
}

- (NSDictionary *)prepareInitialProps
{
  NSMutableDictionary *initProps = [NSMutableDictionary new];

#ifdef RCT_NEW_ARCH_ENABLED
  initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
#endif

  return initProps;
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

#if RCT_NEW_ARCH_ENABLED

#pragma mark - RCTCxxBridgeDelegate

- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
  _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
                                                             delegate:self
                                                            jsInvoker:bridge.jsCallInvoker];
  return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
}

#pragma mark RCTTurboModuleManagerDelegate

- (Class)getModuleClassFromName:(const char *)name
{
  return RCTCoreModulesClassProvider(name);
}

- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
                                                      jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
  return nullptr;
}

- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
                                                     initParams:
                                                         (const facebook::react::ObjCTurboModule::InitParams &)params
{
  return nullptr;
}

- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
  return RCTAppSetupDefaultModuleFromClass(moduleClass);
}

#endif

@end

Podfile

require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

platform :ios, '13.0'

install! 'cocoapods', :deterministic_uuids => false
$RNFirebaseAsStaticFramework = true

target 'mobilityapp' do
  use_expo_modules!
  post_integrate do |installer|
    begin
      expo_patch_react_imports!(installer)
    rescue => e
      Pod::UI.warn e
    end
  end

  # React Native Maps dependencies
#   rn_maps_path = '../node_modules/react-native-maps'
#   pod 'react-native-google-maps', :path => rn_maps_path
  # END React Native Maps dependencies

  config = use_native_modules!

  use_frameworks! :linkage => :static

  # Flags change depending on the env values.
  flags = get_default_flags()

  use_react_native!(
    :path => config[:reactNativePath],

    # Hermes is now enabled by default. Disable by setting this flag to false.
    # Upcoming versions of React Native may rely on get_default_flags(), but
    # we make it explicit here to aid in the React Native upgrade process.
    :hermes_enabled => true,
    :fabric_enabled => flags[:fabric_enabled],
    # Enables Flipper.
    #
    # Note that if you have use_frameworks! enabled, Flipper will not work and
    # you should disable the next line.
    #:flipper_configuration => FlipperConfiguration.enabled, //Due to conflict with RN Firebase
    # An absolute path to your application root.
    :app_path => "#{Pod::Config.instance.installation_root}/.."
  )

  post_install do |installer|
    react_native_post_install(
          installer,
          # Set `mac_catalyst_enabled` to `true` in order to apply patches
          # necessary for Mac Catalyst builds
          :mac_catalyst_enabled => false
        )

#         installer.pods_project.targets.each do |target|
#               target.build_configurations.each do |config|
#                 # Using the un-qualified names means you can swap in different implementations, for example ccache
#                 config.build_settings["CC"] = "clang"
#                 config.build_settings["LD"] = "clang"
#                 config.build_settings["CXX"] = "clang++"
#                 config.build_settings["LDPLUSPLUS"] = "clang++"
#               end
#             end

    __apply_Xcode_12_5_M1_post_install_workaround(installer)
  end
end

What platform(s) does this occur on?

iOS

Environment

expo-env-info 1.0.5 environment info: System: OS: macOS 13.1 Shell: 5.8.1 - /bin/zsh Binaries: Node: 18.10.0 - /usr/local/bin/node Yarn: 1.22.19 - ~/.yarn/bin/yarn npm: 9.2.0 - /usr/local/bin/npm Watchman: 2021.11.15.00 - /usr/local/bin/watchman Managers: CocoaPods: 1.11.2 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: DriverKit 22.2, iOS 16.2, macOS 13.1, tvOS 16.1, watchOS 9.1 IDEs: Android Studio: 2021.3 AI-213.7172.25.2113.9014738 Xcode: 14.2/14C18 - /usr/bin/xcodebuild npmPackages: babel-preset-expo: ~9.2.1 => 9.2.2 expo: ^47.0.0 => 47.0.13 react: 18.1.0 => 18.1.0 react-native: 0.70.5 => 0.70.5 npmGlobalPackages: eas-cli: 2.6.0 expo-cli: 6.1.0 Expo Workflow: bare

Minimal reproducible example

none

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 2
  • Comments: 27 (3 by maintainers)

Most upvoted comments

I was wondering why fresh project works fine with latest expo-modules-core, but my old one doesn’t and dragging AppDelegate.mm to another group and back made it work. It turned out AppDelegate.mm had lastKnownFileType = sourcecode.c.objc in my old project instead of sourcecode.c.objcpp. Check if it solves your problems too.

I don’t know why it’s working but it works. Changing sourcecode.c.objcinto sourcecode.cpp.objcpp fix the problem.

For people who perfer to use the Xcode GUI, you can also change this by having the AppDelegate.mm file open and changing the “Type” option in the right menu’s document tab to “Objective-C++ Source”

image

Note that this is slightly different in the Xcode project file as you are explisitly setting the type, whereas the default is “last known type”, but the effect will be the same in that the #if __cplusplus case will be true and the correct code block will be used.

that’s interesting to me where sourcecode.c.objc for AppDelegate.mm work until now you awesome folks find the issue. if anyone still have sourcecode.c.objc for AppDelegate.mm. please update it rather than removing the #if __cplusplus change. use sourcecode.cpp.objcpp should be correct solution. thanks @nesbite @TomCorvus @TheWirv for the great findings 👏

@guillaume-g

but how to do update the lastKnownFileType please

I searched within ios/Puma.xcodeproj/project.pbxproj for the string sourcecode.c.objc and updated the one that referred to the AppDelegate.mm file. To update it, you literally just swap the string sourcecode.c.objc with sourcecode.cpp.objcpp

Screenshot 2023-02-15 at 14 35 43

I’ve also been having the same issue and can confirm that the following patch gets the build working again for me (Thanks @TheWirv). Let me know if you would like any more info to help identify the root cause @Kudo

(I’m using RN 0.70.6)

expo-modules-core+1.1.1.patch

index 94da477..eacda2c 100644
--- a/node_modules/expo-modules-core/ios/AppDelegates/EXAppDelegateWrapper.h
+++ b/node_modules/expo-modules-core/ios/AppDelegates/EXAppDelegateWrapper.h
@@ -1,7 +1,5 @@
 // Copyright 2018-present 650 Industries. All rights reserved.
 
-#if __cplusplus
-
 #import <UIKit/UIKit.h>
 #import <ExpoModulesCore/EXReactDelegateWrapper.h>
 
@@ -30,12 +28,3 @@ NS_ASSUME_NONNULL_BEGIN
 @end
 
 NS_ASSUME_NONNULL_END
-
-#else
-
-// Workaround the main.m build error when running with new architecture mode
-// Context: https://github.com/facebook/react-native/pull/35661
-@interface EXAppDelegateWrapper : UIResponder
-@end
-
-#endif

@guillaume-g

but how to do update the lastKnownFileType please

I searched within ios/Puma.xcodeproj/project.pbxproj for the string sourcecode.c.objc and updated the one that referred to the AppDelegate.mm file. To update it, you literally just swap the string sourcecode.c.objc with sourcecode.cpp.objcpp

Screenshot 2023-02-15 at 14 35 43

Tks i

I was wondering why fresh project works fine with latest expo-modules-core, but my old one doesn’t and dragging AppDelegate.mm to another group and back made it work. It turned out AppDelegate.mm had lastKnownFileType = sourcecode.c.objc in my old project instead of sourcecode.c.objcpp. Check if it solves your problems too.

I don’t know why it’s working but it works. Changing sourcecode.c.objcinto sourcecode.cpp.objcpp fix the problem.

Tks it has worked for me ❤️

@guillaume-g

but how to do update the lastKnownFileType please

I searched within ios/Puma.xcodeproj/project.pbxproj for the string sourcecode.c.objc and updated the one that referred to the AppDelegate.mm file. To update it, you literally just swap the string sourcecode.c.objc with sourcecode.cpp.objcpp

Screenshot 2023-02-15 at 14 35 43

Just ran into this problem and this fixed it! Thank you so much 🙏

@TheWirv I edited my comment to avoid error, but yes I changed sourcecode.c.objc into sourcecode.cpp.objcpp. I just copied/pasted @nesbite answer because I’m a little lazy when I haven’t had my coffee. Thanks @nesbite btw

This doesn’t really make sense though since this lastKnownFileType is not a valid one. It should be sourcecode.cpp.objcpp; see @nesbite’s last comment and check for the project.pbxproj changes.

@TheWirv my bad, I misspelled it in the comment, should be sourcecode.cpp.objcpp(however sourcecode.c.objcpp worked too).

I mean, it does make sense either way, both @robwalkerco’s and my patch solution as well as changing the lastKnownFileType. Changing the declaration of the file type makes the compiler run in Objective C++ mode which in turn makes it go the #if __cplusplus route which executes the code path that has already been there before.

Since it is mentioned in the upgrade helper, the “change lastKnownFileType to sourcecode.cpp.objcpp” solution is the official one though, in my opinion.

@TomCorvus here, man ☕️ have a good one

@robwalkerco

Thanks, I was afraid to change it directly there, I though it was an option in Xcode!

I was wondering why fresh project works fine with latest expo-modules-core, but my old one doesn’t and dragging AppDelegate.mm to another group and back made it work. It turned out AppDelegate.mm had lastKnownFileType = sourcecode.c.objc in my old project instead of sourcecode.c.objcpp. Check if it solves your problems too.