Search code examples
iosswiftxcodereact-nativemetro-bundler

Invariant Violation and TypeError When Integrating React Native with Existing iOS Swift App


I am currently migrating an iOS app written in Swift to a new React Native project. I successfully integrated a React Native component into my iOS app by first generating a main.jsbundle with the following command:

react-native bundle --dev false --entry-file index.js --bundle-output ios/myApp/main.jsbundle --platform ios --assets-dest iOS

In Swift, I integrated the React Native component using this code:

private func showRNComponent() {
    let rootView = RCTRootView(
        bundleURL: Bundle.main.url(forResource: "main", withExtension: "jsbundle")!,
        moduleName: "DemoPage",
        initialProperties: nil,
        launchOptions: nil
    )
    let vc = UIViewController()
    vc.view = rootView
    self.present(vc, animated: true, completion: nil)
}

This method works fine with the main.jsbundle. However, I attempted to use Metro as a development server for better debugging capabilities and updated the method to fetch the bundle from the development server:

private func showRNComponent() {
    guard let jsCodeLocation = URL(string: "http://localhost:8081/index.bundle?platform=ios") else {
        NSLog("Invalid URL")
        return
    }
    let rootView = RCTRootView(
        bundleURL: jsCodeLocation,
        moduleName: "DemoPage",
        initialProperties: nil,
        launchOptions: nil
    )
    let vc = UIViewController()
    vc.view = rootView
    self.present(vc, animated: true, completion: nil)
}

After starting Metro and running the development server, when trying to trigger the function, I received the following errors in the XCode console:

Invariant Violation: TurboModuleRegistry.getEnforcing(...): 'DevSettings' could not be found. Verify that a module by this name is registered in the native binary. Bridgeless mode: false. TurboModule interop: false. Modules loaded: {"NativeModules":["PlatformConstants","Timing","AppState","SourceCode","BlobModule","WebSocketModule","SettingsManager"],"TurboModules":[],"NotFound":["NativePerformanceCxx","NativePerformanceObserverCxx","LogBox","DevSettings"]}, js engine: hermes
TypeError: Cannot read property 'render' of undefined, js engine: hermes

I suspect this issue might relate to the XCode build target settings, which should be set to Debug mode to load development settings. Even though I have ensured my build settings and preprocessor macros (preprocessor macros: debug=1) are applied across all build configurations, the problem persists.

Here are excerpts from my Podfile and package.json, detailing the libraries and settings I'm using:

//Poddile
source 'https://github.com/CocoaPods/Specs.git'

require Pod::Executable.execute_command('node', ['-p',
  'require.resolve(
    "react-native/scripts/react_native_pods.rb",
    {paths: [process.argv[1]]},
  )', __dir__]).strip

platform :ios, '14.0'
# min_supported_versions = { :ios => "13.0"}
prepare_react_native_project!

target 'MyApp' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  config = use_native_modules!

  use_react_native!(
    :path => config[:reactNativePath],
    :flipper_configuration => FlipperConfiguration.disabled,
    :app_path => "#{Pod::Config.instance.installation_root}/.."
  )

  # Pods for MyApp
  pod 'RxSwift'
  pod 'RxCocoa'
  pod 'SDWebImage'
  pod 'Alamofire'
  pod 'MBProgressHUD'
  pod 'SwiftyJSON'
  pod 'SwiftyUserDefaults'
  pod 'ObjectMapper'
  pod 'Firebase/Crashlytics', '9.2.0'
  pod 'Firebase/Analytics', '9.2.0'
  pod 'Firebase/Messaging', '9.2.0'
  pod 'SwipeCellKit'
  pod 'Kingfisher'
  pod 'WeScan'
  pod 'RxDataSources', '~> 4.0'
  pod 'ChameleonFramework'
#  pod 'FlexLayout'
  pod 'SwiftSoup'
  pod 'IQKeyboardManagerSwift'
  pod 'GoogleMLKit/BarcodeScanning', '2.6.0'
  pod 'MJRefresh'
  pod 'DGCharts'
  
  target 'MyAppTests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'MyAppUITests' do
    inherit! :search_paths
    # Pods for testing
  end


# post install
post_install do |installer|
  react_native_post_install(
      installer,
      config[:reactNativePath],
      :mac_catalyst_enabled => false
    )
  installer.pods_project.targets.each do |target|
      target.build_configurations.each do |config|
        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
      end
    end
  # fix xcode 15 DT_TOOLCHAIN_DIR - remove after fix oficially - https://github.com/CocoaPods/CocoaPods/issues/12065
  installer.aggregate_targets.each do |target|
    target.xcconfigs.each do |variant, xcconfig|
      xcconfig_path = target.client_root + target.xcconfig_relative_path(variant)
      IO.write(xcconfig_path, IO.read(xcconfig_path).gsub("DT_TOOLCHAIN_DIR", "TOOLCHAIN_DIR"))
    end
  end
  
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      if config.base_configuration_reference.is_a? Xcodeproj::Project::Object::PBXFileReference
        xcconfig_path = config.base_configuration_reference.real_path
        IO.write(xcconfig_path, IO.read(xcconfig_path).gsub("DT_TOOLCHAIN_DIR", "TOOLCHAIN_DIR"))
      end
    end
  end
end
end
{
    "name": "MyApp",
    "version": "0.0.1",
    "private": true,
    "scripts": {
        "doctor": "react-native doctor",
        "upgrade": "react-native upgrade",
        "config": "react-native config",
        "android": "react-native run-android",
        "ios": "react-native run-ios --mode \"ETE-t1-Debug\" --scheme=\"MyApp-ETE(t1)\"",
        "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
        "start": "react-native start",
        "bundle:ios": "react-native bundle --dev false --entry-file index.js --bundle-output ios/MyApp/main.jsbundle --platform ios --assets-dest ios",
        "bundle:android": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res",
        "clean": "npx react-native-clean-project"
    },
    "dependencies": {
        "@react-native-async-storage/async-storage": "1.23.1",
        "axios": "^1.6.8",
        "react": "18.2.0",
        "react-native": "0.73.6",
        "react-native-progress": "^5.0.1"
    },
    "devDependencies": {
        "@babel/core": "^7.20.0",
        "@babel/preset-env": "^7.20.0",
        "@babel/runtime": "^7.20.0",
        "@react-native/babel-preset": "0.73.21",
        "@react-native/eslint-config": "0.73.2",
        "@react-native/metro-config": "0.73.5",
        "@react-native/typescript-config": "0.73.1",
        "@types/react": "^18.2.6",
        "@types/react-test-renderer": "^18.0.0",
        "babel-jest": "^29.6.3",
        "eslint": "^8.19.0",
        "prettier": "2.8.8",
        "react-test-renderer": "18.2.0",
        "typescript": "5.0.4"
    },
    "engines": {
        "node": ">=18"
    }
}

Any suggestions on how to resolve these errors or further steps to debug this issue would be highly appreciated!


Solution

  • My problem is solved with adding the custom build config's to the podfile, because my project was using custom build configs.

    Sample code is from https://github.com/CocoaPods/CocoaPods/issues/10015

    enter image description here

    Thank you @Dustin