react-native icon indicating copy to clipboard operation
react-native copied to clipboard

Redefinition of 'Native....SpecJSI' . Error when building with a custom turbo module

Open skinnynpale opened this issue 5 months ago • 14 comments

Description

Redefinition of 'Native....SpecJSI' . Error when building with a custom turbo module. I strictly followed the instructions for create turbo module https://reactnative.dev/docs/the-new-architecture/pillars-turbomodules

image

Steps to reproduce

--

React Native Version

0.73.2

Affected Platforms

Runtime - iOS

Areas

TurboModule - The New Native Module System

Output of npx react-native info

System:
  OS: macOS 14.2.1
  CPU: (12) arm64 Apple M3 Pro
  Memory: 99.55 MB / 18.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 18.19.0
    path: ~/.nvm/versions/node/v18.19.0/bin/node
  Yarn:
    version: 1.22.21
    path: /opt/homebrew/bin/yarn
  npm:
    version: 10.2.3
    path: ~/.nvm/versions/node/v18.19.0/bin/npm
  Watchman:
    version: 2023.12.04.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.14.3
    path: /Users/skinnynpale/.rbenv/shims/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.2
      - iOS 17.2
      - macOS 14.2
      - tvOS 17.2
      - visionOS 1.0
      - watchOS 10.2
  Android SDK: Not Found
IDEs:
  Android Studio: 2023.1 AI-231.9392.1.2311.11076708
  Xcode:
    version: 15.2/15C500b
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.9
    path: /usr/bin/javac
  Ruby:
    version: 3.0.6
    path: /Users/skinnynpale/.rbenv/shims/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.73.2
    wanted: 0.73.2
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: true
  newArchEnabled: true

Stacktrace or Logs

/Users/skinnynpale/Library/Developer/Xcode/DerivedData/a4ord-cnymkqjxmmqjnmfdnnckwsbdoigs/Build/Products/Debug-iphonesimulator/React-Codegen/React_Codegen.framework/Headers/RTNCustomAppBrowserSpec/RTNCustomAppBrowserSpec.h:34:22 Redefinition of 'NativeCustomAppBrowserSpecJSI'

skinnynpale avatar Jan 25 '24 18:01 skinnynpale

hi @skinnynpale can you share the repository you are using?

cipolleschi avatar Jan 25 '24 18:01 cipolleschi

hi @skinnynpale can you share the repository you are using?

sorry, only turbo module https://github.com/skinnynpale/RTNCustomAppBrowser

skinnynpale avatar Jan 25 '24 18:01 skinnynpale

thanks, I'll have a look at this tomorrow morning!

cipolleschi avatar Jan 25 '24 18:01 cipolleschi

thanks, I'll have a look at this tomorrow morning!

I tried running this, it worked https://github.com/deepthisareddy19/turbonativemodules but when I pulled out the turbo module (folder RTNCalculator) and installed it in our application, the error is still the same even with it image

skinnynpale avatar Jan 26 '24 11:01 skinnynpale

Hi @skinnynpale, I tried to repro but I couldn't.

You can find my reproducer here this is public, so you should be able to see it.

I think that the problem is with your specific setup. So, we can investigate it a little:

  1. If you click on the Error in Xcode, it should show you where are the redefinitions, pointing to the various files where they are defined. One definition is in RTNCalculatorSpec.h file. We need to understand where is the other.
  2. Could it be that your app's package.json (not the Turbomodule's one) define a codegenConfig key as well? If yes, it could be that, when running the codegen, we generate the codegen for the library and then the codegen for the app generate the library once again, and we end up with 2 declaration of the JSI interface.

Let me know what you find!

cipolleschi avatar Jan 26 '24 14:01 cipolleschi

Hi @skinnynpale, I tried to repro but I couldn't.

You can find my reproducer here this is public, so you should be able to see it.

I think that the problem is with your specific setup. So, we can investigate it a little:

  1. If you click on the Error in Xcode, it should show you where are the redefinitions, pointing to the various files where they are defined. One definition is in RTNCalculatorSpec.h file. We need to understand where is the other.
  2. Could it be that your app's package.json (not the Turbomodule's one) define a codegenConfig key as well? If yes, it could be that, when running the codegen, we generate the codegen for the library and then the codegen for the app generate the library once again, and we end up with 2 declaration of the JSI interface.

Let me know what you find!

Thank you for your reply!

  1. any ideas? image image image

skinnynpale avatar Jan 29 '24 13:01 skinnynpale

image

Even deleting the "generated" folder from the rtn-calculator itself, where there was another instance creation, did not help

skinnynpale avatar Jan 29 '24 13:01 skinnynpale

Hi @skinnynpale, I tried to repro but I couldn't.

You can find my reproducer here this is public, so you should be able to see it.

I think that the problem is with your specific setup. So, we can investigate it a little:

  1. If you click on the Error in Xcode, it should show you where are the redefinitions, pointing to the various files where they are defined. One definition is in RTNCalculatorSpec.h file. We need to understand where is the other.
  2. Could it be that your app's package.json (not the Turbomodule's one) define a codegenConfig key as well? If yes, it could be that, when running the codegen, we generate the codegen for the library and then the codegen for the app generate the library once again, and we end up with 2 declaration of the JSI interface.

Let me know what you find!

there is no codegenConfig in our project's package.json

skinnynpale avatar Jan 29 '24 14:01 skinnynpale

@cipolleschi oh my...

i deleted -DRCT_NEW_ARCH_ENABLED=1 from section Other C++ Flags under Apple Clang — Custom Compiler flags and commented this line and now the build was compiled

image

image

skinnynpale avatar Jan 29 '24 14:01 skinnynpale

We experience the same in react-native-google-mobile-ads: https://github.com/invertase/react-native-google-mobile-ads/issues/532

dylancom avatar Jan 31 '24 17:01 dylancom

@dylancom can you provide a reproducer?

cipolleschi avatar Jan 31 '24 17:01 cipolleschi

I have the same problem. I did the TurboModule example with RTNCalculator. I did step by step multiple times and it seems that the same error occurs. I also cleaned the npm modules and iOS pod files multiple times.

dumbravaandrei22 avatar Mar 17 '24 11:03 dumbravaandrei22

@dylancom can you provide a reproducer?

@cipolleschi I have faced same as the issue. We can create an empty project and set flag RCT_NEW_ARCH_ENABLED=1 on pod install script and built.

eflashcards avatar Mar 25 '24 08:03 eflashcards

I'm sorry, but I need a reproducer from you, guys. I build new empty apps with React Nativ and the New Architecture every day as that is my main focus, but I can't reproduce this issue in any way. :(

@eflashcards which commands are you using to create an empty project? @dumbravaandrei22 are you sure you followed the tutorial correctly? For example, here skinnynpale deleted a line inadvertently and it make everything fail... @dylancom can you provide a reproducer?

cipolleschi avatar Apr 03 '24 14:04 cipolleschi

@cipolleschi My scripts:

  1. Install Admob:
yarn add react-native-google-mobile-ads
  1. Install pod as Script In package.json from root Project. "pod-i": " cd ios && NO_FLIPPER=1 USE_FRAMEWORKS=static RCT_NEW_ARCH_ENABLED=0 pod install && cd ..",

USE_FRAMEWORDS= static as this: https://docs.page/invertase/react-native-google-mobile-ads#optionally-configure-ios-static-frameworks Also note:

...
use_frameworks! :linkage => :static

$RNFirebaseAsStaticFramework = true
$RNGoogleMobileAdsAsStaticFramework = true
....
flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
  Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
  use_frameworks! :linkage => linkage.to_sym
end
linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
  Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
  use_frameworks! :linkage => linkage.to_sym
end
...
target 'YourApp' do
...
end

eflashcards avatar Apr 05 '24 06:04 eflashcards

I used to fix in my code like this to test and It fixed but not a correct fix @cipolleschi Screenshot 2024-04-05 at 13 21 48

Due to duplicated definition when doing codegen. So It may be from codegen issues - I think so!

eflashcards avatar Apr 05 '24 06:04 eflashcards

@eflashcards thanks for the reproducer steps and the heads up with the pragmas. It might be that we forgot to add #pragma once in the codegen? 🤔

cipolleschi avatar Apr 05 '24 08:04 cipolleschi

Yes @cipolleschi . I used to code objective-c & C/C++. When we include a header file. We must check If the class defined or not to avoid duplication from #import / #include from two other places.

  1. If the Class A has defined in File B (import A.h), we should skip in the File C (import A.h). Preprocessor format:

#ifndef A_H #define A_H

class A { ... }

#endif

eflashcards avatar Apr 05 '24 09:04 eflashcards

I managed to repro it locally! :D

One think I noticed is that both RNTCalculator.h and RNTCalculator.mm both import the same file.

  • RNTCalculator.h
+#import <RNTCalculatorSpec/RNTCalculatorSpec.h>

NS_ASSUME_NONNULL_BEGIN

@interface RNTCalculator : NSObject <NativeCalculatorSpec>
  • RNTCalculator.mm
+#import "RNTCalculatorSpec.h"
#import "RNTCalculator.h"

@implementation RNTCalculator
// ...

So, what happens is that, when Xcode builds the RNTCalculator.mm, it takes the RNTCalculatorSpec once because it has been explicitly imported by line 1, and then it takes it another time because the #import "RNTCalculator.h" import the RNTCalculatorSpec transitively.

The first line can be removed:

-#import "RNTCalculatorSpec.h"
#import "RNTCalculator.h"

@implementation RNTCalculator
// ...

And everything will work fine ==> The error goes away.

That said, this is a bit brittle. I'd like to see if we can find a systematic approach to solve this.

cc. @eflashcards @dylancom @dumbravaandrei22 @skinnynpale @javache

We'll probably end up applying the @eflashcards suggestions in codegen directly. For some reasons, #pragma once does not work in this case.

cipolleschi avatar Apr 09 '24 16:04 cipolleschi

For some reasons - I found a quick fix for the error on React Native Google Mobile Ads by this patch:

diff --git a/node_modules/react-native-google-mobile-ads/ios/RNGoogleMobileAds/RNGoogleMobileAdsModule.mm b/node_modules/react-native-google-mobile-ads/ios/RNGoogleMobileAds/RNGoogleMobileAdsModule.mm
index 9e48dec..8c25534 100644
--- a/node_modules/react-native-google-mobile-ads/ios/RNGoogleMobileAds/RNGoogleMobileAdsModule.mm
+++ b/node_modules/react-native-google-mobile-ads/ios/RNGoogleMobileAds/RNGoogleMobileAdsModule.mm
@@ -21,9 +21,9 @@
 #import <React/RCTUtils.h>
 
 #import "RNGoogleMobileAdsModule.h"
-#ifdef RCT_NEW_ARCH_ENABLED
-#import "RNGoogleMobileAdsSpec.h"
-#endif
+// #ifdef RCT_NEW_ARCH_ENABLED
+// #import "RNGoogleMobileAdsSpec.h"
+// #endif
 #import "common/RNSharedUtils.h"
 
 @implementation RNGoogleMobileAdsModule

This line makes duplicated code: https://github.com/invertase/react-native-google-mobile-ads/blob/d438bf24e1caa479570772be8a78c35ac09cebe5/ios/RNGoogleMobileAds/RNGoogleMobileAdsModule.mm#L25 While It is importing here: https://github.com/invertase/react-native-google-mobile-ads/blob/d438bf24e1caa479570772be8a78c35ac09cebe5/ios/RNGoogleMobileAds/RNGoogleMobileAdsModule.h#L22C1-L22C56

@cipolleschi @dylancom Though I think that should be fixed from the codegen. But some magic thing is from the import. When we import header file in .m and .mm files => #pragma once is not working is in case. My suggestion: We should only import xxxxSpec.h or xxxxTurbo.h in the header file. Then we import the module's header file in to main module (.mm) file.

I.E:

// MyNativeModule.h 
#pragma once
... import your turbo.h or Specs.h files here

// MyNativeModule.mm
#import "MyNativeModule.h"
...Please don't import  your turbo.h or Specs.h files here

=> Because in my project now - My app is still crash for some reasons which I am investigating. But I can build by my above patch.

eflashcards avatar Apr 09 '24 23:04 eflashcards

Yes, #pragma once doesn't seem to work on iOS when using codegen.

I do agree to everything @eflashcards mentioned:

  • I fixed the docs, here, to make sure that there is only one import of those files.
  • I'm working on a Codegen change to add the #include guards here.

Hopefully, we will be able to fix this for good soon and systematically..

cipolleschi avatar Apr 10 '24 11:04 cipolleschi