Bug: C++ types from imported module '__ObjC' do not support library evolution
How were you trying to build the app?
Build Type
Xcode build (cmd+b)
I also tried running the project with the following command, similarly as the example app build:ios
xcodebuild -workspace SharedMobileModules.xcworkspace -scheme ReactSharedModules -skipPackagePluginValidation -skipMacroValidation -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO
Project Context
Developing and building a Brownfield React Native Framework that is published through SPM and AAR Artifactory. The error happens when building the famework target on Xcode.
- iOS Version: 16.6
- Xcode version: 16.2
Debugging
I tried disabling the Xcode build setting Build Libraries for Distribution. This is already set to No on pods. However, this might not be ideal in our case because we distribute our Brownfield React Native setup as a SPM package.
Full build logs
Build ReactSharedModules_2025-06-13T14-38-50.txt
Update
I also checked the Swift Compiler - Language and changed it to be C++ / Objective-C++ instead of C / Objective-C. This helped. I have a different error output now:
Project dependencies
// Root package.json dependencies
"devDependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^5.1.1",
"@eslint-react/eslint-plugin": "^1.40.1",
"@graphql-codegen/cli": "^5.0.5",
"@lodev09/react-native-true-sheet": "2.0.3",
"@parcel/watcher": "^2.5.1",
"@react-native-masked-view/masked-view": "0.3.2",
"@react-native/eslint-config": "^0.73.1",
"@react-navigation/native": "^7.0.13",
"@react-navigation/native-stack": "^7.1.14",
"@testing-library/react-native": "^12.9.0",
"@types/jest": "^29.5.14",
"@types/lodash": "^4",
"@types/react": "^18.2.44",
"@types/react-test-renderer": "^18",
"@types/sanitize-html": "2",
"@typescript-eslint/eslint-plugin": "^8.29.0",
"@typescript-eslint/parser": "^8.29.0",
"@zest/react-native": "0.0.49",
"ajv-cli": "^5.0.0",
"eslint": "^8.51.0",
"eslint-config-prettier": "^9.0.0",
"eslint-import-resolver-typescript": "^4.3.1",
"eslint-plugin-custom-rules": "workspace:^",
"eslint-plugin-design-system-tracker": "workspace:^",
"eslint-plugin-filenames-simple": "^0.9.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^28.9.0",
"eslint-plugin-prefer-arrow": "^1.2.3",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-native": "^5.0.0",
"eslint-plugin-testing-library": "^7.0.0",
"glob": "^11.0.0",
"husky": "^9.1.7",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"kleur": "^4.1.5",
"lint-staged": "^15.2.10",
"nitro-codegen": "^0.26.2",
"plop": "^4.0.1",
"prettier": "^3.0.3",
"quicktype": "^23.0.170",
"react": "18.3.1",
"react-native": "0.75.4",
"react-native-builder-bob": "^0.30.2",
"react-native-nitro-modules": "^0.26.2",
"react-native-pager-view": "6.7.0",
"react-native-reanimated": "3.16.7",
"react-native-restart": "^0.0.27",
"react-native-safe-area-context": "4.14.1",
"react-native-screens": "^4.3.0",
"react-native-svg": "15.10.0",
"react-test-renderer": "18.3.1",
"ts-node": "^10.9.2",
"typescript": "5.1.6"
},
"dependencies": {
"@apollo/client": "^3.13.6",
"@lokalise/node-api": "^14.0.0",
"@opentelemetry/core": "2.0.1",
"@opentelemetry/exporter-trace-otlp-http": "0.201.1",
"@opentelemetry/instrumentation-fetch": "0.201.1",
"@opentelemetry/instrumentation-xml-http-request": "0.201.1",
"@opentelemetry/otlp-exporter-base": "0.201.1",
"@opentelemetry/resources": "2.0.1",
"@opentelemetry/sdk-trace-base": "2.0.1",
"@opentelemetry/sdk-trace-web": "2.0.1",
"@opentelemetry/semantic-conventions": "1.33.1",
"@tanstack/react-query": "^5.59.16",
"date-fns": "4.1.0",
"graphql": "^16.10.0",
"i18next": "24.2.1",
"lodash": "^4.17.21",
"react-i18next": "15.4.0",
"react-native-device-info": "14.0.4",
"react-native-render-html": "6.3.4",
"react-native-url-polyfill": "2.0.0",
"sanitize-html": "2.17.0",
"zod": "^3.23.8",
"zustand": "^5.0.3",
"zx": "^8.4.0"
}
// App/Framework package dependencies
"dependencies": {
"@lodev09/react-native-true-sheet": "*",
"@react-native-masked-view/masked-view": "*",
"@react-navigation/native": "*",
"@react-navigation/native-stack": "*",
"@zest/react-native": "*",
"react": "*",
"react-native": "*",
"react-native-device-info": "*",
"react-native-nitro-modules": "*",
"react-native-pager-view": "*",
"react-native-reanimated": "*",
"react-native-restart": "*",
"react-native-safe-area-context": "*",
"react-native-screens": "*",
"react-native-svg": "*"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native/babel-preset": "0.75.4",
"@react-native/metro-config": "0.75.4",
"@react-native/typescript-config": "0.75.4",
"react-native-builder-bob": "^0.30.2"
}
Nitro Modules Version
^0.26.2
Nitrogen Version
^0.26.2
Target platforms
iOS
Operating system
MacOS
Can you build the Nitro Modules Example app?
No, I cannot build the Example app either
I tried running in the example app, but ran into issues with React Codegen failing. I spent too much time on trying to get the example app also working.
Additional information
- [ ] I am using Expo
- [x] I am using Nitrogen (nitro-codegen)
- [x] I have read and followed the Troubleshooting Guide.
- [ ] I created a reproduction PR to reproduce this issue here in the nitro repo. (See Contributing for more information)
- [x] I searched for similar issues in this repository and found none.
Hm I'm not sure if I understand the problem properly.
Is there any way for you to separate your SPM part from the Nitro Module? I'm pretty sure it's the new C++ interop in the Swift Compiler that's kinda spazzing out with the stuff you added to it. Separating it into two frameworks will fix that
Thanks for checking this out @mrousavy. That is the setup we have. We added a local podspec for the Nitro module. Once we execute pod install, we obtain the NitroModule as an external dependency / Framework, which we then link or import into our SPM framework.
It seemed the problem was related to the following issue.
Adding the following in our local podspec fixed the compilation issues in NitroExternalNativeModules:
s.pod_target_xcconfig = {
"HEADER_SEARCH_PATHS" => [
"${PODS_ROOT}/RCT-Folly",
],
"GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES",
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
}
Unfortunately, it seems like after fixing other setup issues and compilation issues, the main XCFranework / Target is having issues with Folly
I added the GCC_PREPROCESSOR_DEFINITIONS and OTHER_CPLUSPLUSFLAGS in our XCFramework / SPM.
I also added the following to the podspec, which seems the Lottie-react-native package had a similar issue as well
s.requires_arc = true
s.dependency "React-Core"
It got rid of the Folly and React errors,.
Now I'm getting a build log issue that the generated code/symbol is duplicated:
duplicate symbol 'protocol witness for Cxx.CxxPair.first.modify : A.First in conformance __C.std.__1.pair<std.__1.basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>_const, std.__1.variant<std.__1.basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>, CDouble, CBool>> : Cxx.CxxPair in __C_Synthesized' in:
/Users/serjagopian/Library/Developer/Xcode/DerivedData/SharedMobileModules-bqqdssxheojywccubsqrrqwdvfye/Build/Products/Debug-iphonesimulator/NitroExternalNativeModules/NitroExternalNativeModules.framework/NitroExternalNativeModules[15](OpenScreenEvent.o)
/Users/serjagopian/Library/Developer/Xcode/DerivedData/SharedMobileModules-bqqdssxheojywccubsqrrqwdvfye/Build/Products/Debug-iphonesimulator/NitroExternalNativeModules/NitroExternalNativeModules.framework/NitroExternalNativeModules[8](AnalyticsEvent.o)
duplicate symbol 'protocol witness for Cxx.CxxPair.first.setter : A.First in conformance __C.std.__1.pair<std.__1.basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>_const, std.__1.variant<std.__1.basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>, CDouble, CBool>> : Cxx.CxxPair in __C_Synthesized' in:
/Users/serjagopian/Library/Developer/Xcode/DerivedData/SharedMobileModules-bqqdssxheojywccubsqrrqwdvfye/Build/Products/Debug-iphonesimulator/NitroExternalNativeModules/NitroExternalNativeModules.framework/NitroExternalNativeModules[15](OpenScreenEvent.o)
/Users/serjagopian/Library/Developer/Xcode/DerivedData/SharedMobileModules-bqqdssxheojywccubsqrrqwdvfye/Build/Products/Debug-iphonesimulator/NitroExternalNativeModules/NitroExternalNativeModules.framework/NitroExternalNativeModules[8](AnalyticsEvent.o)
ld: 2 duplicate symbols
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I suspect this might have to do that we build our pods as static libraries instead of dynamic. I will try to exclusively build the NitroExternalNativeModules as a dynamic library.
@mrousavy by the way, I noticed while using npx create-react-native-library@latest, the podspec file doesn't have the same template as you have here. Specifically it is missing the following:
s.source_files = [
# Implementation (Swift)
"ios/**/*.{swift}",
# Autolinking/Registration (Objective-C++)
"ios/**/*.{m,mm}",
# Implementation (C++ objects)
"cpp/**/*.{hpp,cpp}",
]
s.pod_target_xcconfig = {
# C++ compiler flags, mainly for folly.
"GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES"
}
s.dependency 'React-jsi'
s.dependency 'React-callinvoker'
This is the template you get ouside the box with the CLI:
require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
Pod::Spec.new do |s|
s.name = "NitroLibrary"
s.version = package["version"]
s.summary = package["description"]
s.homepage = package["homepage"]
s.license = package["license"]
s.authors = package["author"]
s.platforms = { :ios => min_ios_version_supported }
s.source = { :git => "https://github.com/serjooo/nitro-module-example.git.git", :tag => "#{s.version}" }
s.source_files = "ios/**/*.{h,m,mm,swift}"
load 'nitrogen/generated/ios/NitroLibrary+autolinking.rb'
add_nitrogen_files(s)
install_modules_dependencies(s)
end
Oh well I guess then we need to add that to the template - can you create a PR in create-react-native-libray? thanks
@mrousavy already done ✅. I was wondering if we should have a smarter process to keep both templates in sync. It's not ideal that we have two sources of truth for the template. One idea I have is that the other tool pulls the template folder from this repository while genearting a nitro-module library.
By the way, I'm still facing some compilation issues. I haven't had the time to debug further, but I'm hoping to be able to reproduce it in a controlled smaller project. I'll keep this thread updated once I get back to it.
By the way, I'm still facing some compilation issues. I haven't had the time to debug further, but I'm hoping to be able to reproduce it in a controlled smaller project. I'll keep this thread updated once I get back to it.
Thank you! Sorry I can't be of better help - but I cannot reproduce this and I honestly don't have a lot of free time now... so if you find a solution to this, this would be hugely appreciated.
does this fix anything? https://github.com/mrousavy/nitro/pull/702
nvm I think we don't want library evolution. Swift's C++ interop does not seem to support it for types imported from C++, so we need to disable it. It's disabled by default, maybe you switched it on?
Hey @mrousavy, thanks for checking that out. I probably did enable it because we plan to distribute the Brownfield library integration, but it might work without it being turned on as well. I'm currently focused on another topic and have deprioritized this. Also, no ETA when I'll be able to check it out again given some personal life changes. However, definitely we will need to have this done sooner or later, so I will follow up on the issue and try to debug further.
Note, this might be related to https://forums.swift.org/t/error-when-cross-compiling-swift-package-with-cxx-interop-enabled-with-swift-6-2/81330
Hey - I think all of the issues have been fixed now!
- The
duplicate symbol 'protocol witness for Cxx.CxxPair.first.modify : A.Firsthas been fixed in https://github.com/mrousavy/nitro/pull/814 - The
folly/folly-config.h file not founderror has been fixed - Library evolution should work now
Hey Marc, I will try testing things out over the next few days to see if it works as expected! Thank you for following up and finding the root issues!
I tested it out, it seems the errors have been fixed, but we get a new error that it can't find 'BorrowingReference.hpp' file not found
where? i need more context. which file? that header is 100% public. maybe you need to do #include <NitroModules/BorrowingReference.hpp> instead
Sorry for not providing more context. It is in NitroModules/cpp/core/ArrayBuffer.h.
that header is 100% public
Yea I also checked this in NitroModules, and I noticed that specific header file is included under Project and not Public, which I think should still be exposed
Side topic, might/should be a new issue, but I was trying to create a new Nitro Project to have a reproducible project where we can actively debug there, and while creating the project using the react-native-builder-bob, I noticed that Xcode complains that you need to enable C++ interop in the example project when trying to import the NitroModule.
After doing that, you also get compiler errors.
The environemnt is the following:
- Xcode 16.4
- iOS 15.1 (bob-builder default)
After changing the interop build setting, we get the following build errors:
Yea I also checked this in NitroModules, and I noticed that specific header file is included under Project and not Public, which I think should still be exposed
Thanks this is super helpful - to fix that issue we would need to add BorrowingReference (and OwningReference and any other header it starts to include) to the .public_header_files in the podspec:
https://github.com/mrousavy/nitro/blob/e2220019ea129da832546a926ff6d485f71220c7/packages/react-native-nitro-modules/NitroModules.podspec#L33-L58
..maybe like this:
s.public_header_files = [
"cpp/**/*.hpp",
"ios/**/*.hpp"
]
Not sure if this is what we want to ship. Alternatively, we can forward-declare it, but then we cannot use any methods of it inside header files - only inside .cpp files.
I noticed that Xcode complains that you need to enable C++ interop in the example project when trying to import the NitroModule.
Ah yea, I think honestly that importing NitroModules is not really recommended in the example app. It's quite tricky to set up, and since React Native has so many custom C++ flags it's so easy that those break in between. That's why I keep all Nitro Modules separate projects, that keeps things safe.
I can try that out and opening a PR later.
importing NitroModules is not really recommended in the example app
Just to make sure we are on the same page, I don't mean importing NitroModules pod itself, but the generated NitroModule / library in this case. In the screenshot, the library is called ReactSharedModules.
If this becomes complicated, and making it compile is hard, then maybe Nitro isn't the right tool for us to solve our problem, because what we are trying to do in our case is remove all the React Native bridging API we have over from the old architecture directly to Nitro. We are currently stuck on RN version 0.75.4. We want to migrate to the new React Native version ideally 0.79.1, and instead of using TurboModules use NitroModules.
Before doing the update of RN, we want to get ahead of doing the breaking changes iteratively using Nitro because the minimum requirement for Nitro is RN version 0.75 and up.
Here is a high-level diagram what we are doing and part of the diagram is how things are working right now for us in the Brownfield setup:
As you can see from the diagram, the NitroBridgeModule needs to be a dependency of the Demo/Shell application, and especially the framework. This way, the production iOS application can interface with those APIs and call them (ex. telling RN realm that a selection finished or a data got updated) or injecting implementation of 3rd party libraries (ex. google analytics is implemented there and the nitro module or native module exposes API to make a trackAnalytics call for the RN realm.
Hm. I guess this workflow should be supported - you should be able to import a Nitro Module (and technically also NitroModules core) from your app's project. I don't know why that doesn't work yet...
..maybe like this:
s.public_header_files = [ "cpp//*.hpp", "ios//*.hpp" ]
I had to write something similar, can confirm this helps
After changing the interop build setting, we get the following build errors:
Adding this to my Podfile seems to have fixed the issue with glog
installer.pods_project.targets.each do |target|
if target.name == 'glog'
target.build_configurations.each do |config|
config.build_settings['DEFINES_MODULE'] = 'NO'
config.build_settings['CLANG_ENABLE_MODULES'] = 'NO'
config.build_settings['CLANG_ENABLE_MODULE_MAP'] = 'NO'
end
end
Can't remember why (I haven't touched this in a month) but I actually have if target.name == 'glog' || target.name == 'React-cxxreact', try it if you run into a different issue.
I'm personally stuck with (and gave up at that point):
examples/ios-react/ios/kit/temp/Debug/iphonesimulator/DerivedData/Build/Intermediates.noindex/ArchiveIntermediates/ReactNativeKit/BuildProductsPath/Debug-iphonesimulator/React-runtimescheduler/React_runtimescheduler.framework/Headers/react/renderer/runtimescheduler/SchedulerPriorityUtils.h:16:39: error: missing '#include "ReactCommon/SchedulerPriority.h"'; 'SchedulerPriority' must be declared before it is used
So importing Nitro lib in the app -> pulls Nitro -> requires objcxx interop -> breaks Once you fix all Nitro-related issues, C++ interop issues seem to occur as soon as React_RCTAppDelegate is pulled in I'm somewhat convinced it's no longer a Nitro issue past that point
@serjooo I've noticed you've checked out react-native-kit
The main branch has a hacky way to circumvent the issue by not import the Nitro lib directly.
You can't really import nitrogen'd types so you have to rely on "basic" ones (untyped maps are okay; callbacks work too). Not a true solution, but if you need a way so pass some data back and forth between RN & Swift that works right now, this is the way to go.
The main-x branch contains my latest (?) attempt to actually import it.
This is a fun debugging journey. Let me know what you find out, I think this feature is wanted by many users. There has to be a way to safely include this in an app.
Could be that we need to set special flags in the React pods so they don't break when referenced. Or even a Swift bug? Idk.
Just tried to install this lib in our project, got this
❌ (/Users/adomassileikis/repos/compass-app/ios/Pods/RCT-Folly/folly/portability/Config.h:20:10)
18 |
19 | #ifndef FOLLY_NO_CONFIG
> 20 | #include <folly/folly-config.h>
| ^ 'folly/folly-config.h' file not found
21 | #endif
22 |
23 | #if __has_include(<features.h>)
❌ (/Users/adomassileikis/repos/compass-app/node_modules/react-native-nitro-modules/cpp/core/ArrayBuffer.hpp:11:10)
9 |
10 | #include "BorrowingReference.hpp"
> 11 | #include <jsi/jsi.h>
| ^ could not build module 'jsi'
12 | #include <thread>
13 | #include <vector>
14 |
❌ (/Users/adomassileikis/repos/compass-app/node_modules/react-native-ble-nitro/nitrogen/generated/shared/c++/AndroidScanMode.hpp:11:10)
9 |
10 | #if __has_include(<NitroModules/JSIConverter.hpp>)
> 11 | #include <NitroModules/JSIConverter.hpp>
| ^ could not build module 'NitroModules'
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
with "react-native-nitro-modules": "^0.29.2"
I am doing something very similar for a Brownfield app, @serjooo did you manage to turn on BUILD_LIBRARY_FOR_DISTRIBUTION? If I turn it on I keep getting messages like C++ types from imported module '__ObjC' do not support library evolution, I saw @mrousavy mentioning that library evolution should be supported now so not sure if I'm missing something silly.
Thanks!
Hey @BrandonMA, we dropped trying to use NitroModules for the Brownfield setup unfortunately. We are on our way to migrate everything to React Native, so for now we are living with the old architecture, and avoiding migrations to Turbo Modules and Nitro Modules.