🔥 [🐛] iOS build fails with use_frameworks! and pnpm due to React-bridging header path issue
Reproduction and further description: https://github.com/evelant/test-pnpm-ios-headers-path-bug
Sorry to bother you with this one @mikehardy since it isn't technically an RNFirebase issue but users of RNFirebase are most likely to run into this issue and I think you're probably the most likely person to have some insight about solving it.
Also filed at https://github.com/facebook/react-native/issues/34742 and https://github.com/expo/expo/issues/19200
Issue
iOS react-native header path failure with use_frameworks! and pnpm
When using pnpm as a package manager along with use_frameworks! iOS builds fail because the header files for
the React-bridging pod don't get copied to the correct directory.
They should end up at iOS/Pods/Headers/Private/React-bridging/react/bridging
but they actually end up at ios/build/Build/Products/Debug-iphonesimulator/React-bridging/.pnpm/[email protected]_xbangyd2oalr524ah376m5q3oi_tf4i3rco7go2qrnsv66v2274bm/node_modules/react-native/ReactCommon/react/bridging
.
I'm not sure if this is specific to EAS build but that's what I use so I made the repro using it.
Motivation
use_framewoks! is a requirement for react-native-firebase v15 and up since the underlying
google libraries have a hard requirement for it. I need to use v15+ in my app to support firebase cloud functions v2.
pnpm is used because I've been bitten by "ghost dependencies" too many times in a monorepo. Hoisting all packages to
root level and allowing any package to be resolved if it just happens to be required transitively by any project in
the repo is a recipe for pain. pnpm fixes that by disallowing resolution of undeclared dependencies,
requiring correct peer deps, and not hoisting anything. See below for tweaks needed to work around metro deficiencies.
To reproduce
- npm install -g pnpm
- npm install -g eas-cli
- pnpm install
- pnpm build-local
Observe the build failing. Result is a temp folder output at the end of build. Open it with XCode to see the mangled path in the build phases of the React-bridging pod.
Manual fix
If you change the copy phase of the React-bridging pod to an absolute path of
$(PODS_ROOT)/Headers/Private/React-bridging/react/bridging
the headers get copied to the correct location. This fixes building of the pods project but the main project still
fails.
Add $(PODS_ROOT)/Headers/Private/React-bridging to the main project header search paths and the project will finally
build.
Possibly related issues
https://github.com/facebook/react-native/issues/34102
https://github.com/CocoaPods/CocoaPods/issues/2382
https://github.com/CocoaPods/CocoaPods/issues/6603
https://github.com/CocoaPods/CocoaPods/pull/9451
https://github.com/CocoaPods/CocoaPods/issues/5790
pnpm setup
To make pnpm work with react-native there are a couple of changes to the expo defaults for this project
-
metro.config.jsuses @rnx-kit/metro-resolver-symlinks to work around lack of symlink support in metro -
.npmrccontains a couplepublic-hoist-patternentries to ensure some undeclared dependencies are resolvable in node_modules. - There are a few dependencies added to devDependencies since pnpm doesn't allow undeclared dependencies to be resolved, they need to be stated explicitly instead of hopefully/maybe installed as a transitive dep.
Fixes attempted
-
patches/[email protected]fixes the header path ofReact-bridgingper react-native #34102 -
update_ios_podfile_plugin.jsmodifies thePodfileto work around CocoaPods #5970 (not sure if entirely necessary)
Per the above reproduction description the build can be made to work by manually fixing the copy step of the
React-bridging pod and manually adding a header search path to the root project.
I'm not sure how to apply proper fixes for either of those issues.
Project Files
Javascript
Click To Expand
package.json:
https://github.com/evelant/test-pnpm-ios-headers-path-bug/blob/main/package.json
firebase.json for react-native-firebase v6:
# N/A
iOS
Click To Expand
ios/Podfile:
- [ ] I'm not using Pods
- [x] I'm using Pods and my Podfile looks like:
From output generated by EAS build
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
require File.join(File.dirname(`node --print "require.resolve('@react-native-community/cli-platform-ios/package.json')"`), "native_modules")
require 'json'
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
# @generated begin rn-firebase-use-frameworks-hacks - expo prebuild (DO NOT MODIFY) sync-f5cdc2d7bd4a6aab702edc22de7a304de10bf620
$RNFirebaseAsStaticFramework = true
# @generated end rn-firebase-use-frameworks-hacks
platform :ios, podfile_properties['ios.deploymentTarget'] || '12.4'
install! 'cocoapods',
:deterministic_uuids => false
target 'testpnpmiosheaderspathbug' do
use_expo_modules!
config = use_native_modules!
use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
# Flags change depending on the env values.
flags = get_default_flags()
use_react_native!(
:path => config[:reactNativePath],
:hermes_enabled => flags[:hermes_enabled] || podfile_properties['expo.jsEngine'] == 'hermes',
:fabric_enabled => flags[:fabric_enabled],
# An absolute path to your application root.
:app_path => "#{Dir.pwd}/.."
)
# Uncomment to opt-in to using Flipper
# Note that if you have use_frameworks! enabled, Flipper will not work
#
# if !ENV['CI']
# use_flipper!()
# end
post_install do |installer|
# @generated begin rn-firebase-use-frameworks-hacks-header-paths - expo prebuild (DO NOT MODIFY) sync-9599ef219643e9a860d65eac898dc1064e3a8d21
#Fix bug where headers get set as "Project" files instead of "Public" when cocoapods traverses symlinks
installer.pods_project.targets.each do |target|
puts "target ? #{target.name}"
if (target.respond_to?(:headers_build_phase) && target.name.include?("React-bridging"))
puts "target has headers build phase, setting public attrs"
target.headers_build_phase.files.each do |file|
puts "setting attributes on header build phase #{file.file_ref.name}"
file.settings = { 'ATTRIBUTES' => ['Public'] }
end
end
end
#Fix search paths for React-bridging
installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
target_installation_result.native_target.build_configurations.each do |config|
# For third party modules who have React-bridging dependency to search correct headers
config.build_settings['HEADER_SEARCH_PATHS'] ||= '$(inherited) '
config.build_settings['HEADER_SEARCH_PATHS'] << '"$(PODS_ROOT)/Headers/Private/React-bridging" '
config.build_settings['HEADER_SEARCH_PATHS'] << '"$(PODS_CONFIGURATION_BUILD_DIR)/React-bridging/_.framework/Headers" '
end
end
# @generated end rn-firebase-use-frameworks-hacks-header-paths
react_native_post_install(installer)
__apply_Xcode_12_5_M1_post_install_workaround(installer)
# This is necessary for Xcode 14, because it signs resource bundles by default
# when building for devices.
installer.target_installation_results.pod_target_installation_results
.each do |pod_name, target_installation_result|
target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
resource_bundle_target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
end
end
post_integrate do |installer|
begin
expo_patch_react_imports!(installer)
rescue => e
Pod::UI.warn e
end
end
end
AppDelegate.m:
// N/A
Android
Click To Expand
Have you converted to AndroidX?
- [ ] my application is an AndroidX application?
- [ ] I am using
android/gradle.settingsjetifier=truefor Android compatibility? - [ ] I am using the NPM package
jetifierfor react-native compatibility?
android/build.gradle:
// N/A
android/app/build.gradle:
// N/A
android/settings.gradle:
// N/A
MainApplication.java:
// N/A
AndroidManifest.xml:
<!-- N/A -->
Environment
Click To Expand
react-native info output:
System:
OS: macOS 12.5.1
CPU: (10) arm64 Apple M1 Pro
Memory: 234.75 MB / 16.00 GB
Shell: 0.68.1 - /Users/imagio/.cargo/bin/nu
Binaries:
Node: 16.17.0 - ~/Library/Caches/fnm_multishells/47864_1663586667411/bin/node
Yarn: 1.22.19 - ~/Library/Caches/fnm_multishells/47864_1663586667411/bin/yarn
npm: 7.18.1 - ~/Library/Caches/fnm_multishells/47864_1663586667411/bin/npm
Watchman: Not Found
Managers:
CocoaPods: 1.11.3 - /Users/imagio/.asdf/shims/pod
SDKs:
iOS SDK:
Platforms: DriverKit 21.4, iOS 16.0, macOS 12.3, tvOS 16.0, watchOS 9.0
Android SDK: Not Found
IDEs:
Android Studio: Dolphin 2021.3.1 Dolphin 2021.3.1
Xcode: 14.0/14A309 - /usr/bin/xcodebuild
Languages:
Java: 17.0.4.1 - /usr/bin/javac
npmPackages:
@react-native-community/cli: Not Found
react: 18.0.0 => 18.0.0
react-native: 0.69.5 => 0.69.5
react-native-macos: Not Found
npmGlobalPackages:
*react-native*: Not Found
-
Platform that you're experiencing the issue on:
- [X ] iOS
- [ ] Android
- [X ] iOS but have not tested behavior on Android
- [ ] Android but have not tested behavior on iOS
- [ ] Both
-
react-native-firebaseversion you're using that has this issue:- N/A -- issue is with use_frameworks!
-
Firebasemodule(s) you're using that has the issue:-
e.g. Instance ID
-
-
Are you using
TypeScript?- Y - 4.8.3
- 👉 Check out
React Native FirebaseandInvertaseon Twitter for updates on the library.
While this is a most excellent write up - it really is, I just don't see how this is actionable here?
It isn't. This isn't a problem with react-native-firebase but I think it's likely to get filed here again with a less through description since rnfirebase seems to be the primary driver of needing use_frameworks! at the moment.
I was hoping you might have some insight on how to deal with cocoapods copy paths since you've dealt with a lot of use_frameworks! issues so far. I'm kinda stuck at this point.
If not at least I hope it serves as a heads up for you. Feel free to close the issue if its not relevant enough here.
We definitely are the driver for use_frameworks at the moment, and I think you're right that it has a lot of value since it has such a great description. I'll leave it open as even though it seems like an in-between issue (as I mentioned on the related react-native issue) this may not be the home for it but may also be the best home for discussion and I'm happy to host it. Just can't move it forward. I can collaborate on any fixes or ideas though if that helps, I do like making things work, I just can't own this one
I need to use v15+ in my app to support firebase cloud functions v2.
Hi @evelant, apologies for highjacking the thread, but have you managed to get the callable functions v2 working with rnfb? Related to [Feature Request] Add support for Cloud Functions v2
@atanaskanchev mostly likely not yet, that one is still pending 😄 - note that you can do this:
// We should use functions().getHttpsCallableFromURL but that is firebase-js-sdk v9 only
// So call directly:
const token = await auth().currentUser!!.getIdToken();
const functionsAPIURL = `PUT YOUR FUNCTION URL HERE - I HAVE A FUNCTION THAT RETURNS URL, USES EMULATOR IN AS NEEDED TOO`
const result = await fetch(functionsAPIURL, {
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token,
},
method: 'post',
body: JSON.stringify({
data: {
// WHATEVER YOUR DATA IS HERE, I THINK IT *MUST* BE WRAPPED IN `data` LIKE THIS, BUT TEST IT
},
}),
});
const data = await result.json();
console.log('calculate result:', result);
if (data?.error) {
console.log('data: ', JSON.stringify(data, null, 2));
console.log('error: ', JSON.stringify(data.error, null, 2));
throw new Error(data.error);
}
I'm using this actual code (without the SCREAMING LOOK AT ME COMMENTS at least) in production and it works
Thanks @mikehardy
FWIW I modified make_demo.sh here to reproduce this issue in non-expo react-native https://github.com/evelant/rnfbdemo
make_demo.sh will fail to compile on iOS with the same issue -- React-bridging headers are copied to wrong path
Can somebody please help me with this? I tried to apply the above fix from @evelant and I am still experiencing the "RCTBridge.h" not found error.
Here is my podfile:
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
$RNFirebaseAsStaticFramework = true
platform :ios, '12.4'
install! 'cocoapods', :deterministic_uuids => false
target 'CampusConnect' do
use_frameworks! :linkage => :static
config = use_native_modules!
# 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.disabled,
# An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
target 'CampusConnectTests' do
inherit! :complete
# Pods for testing
end
post_install do |installer|
#Fix bug where headers get set as "Project" files instead of "Public" when cocoapods traverses symlinks
installer.pods_project.targets.each do |target|
puts "target ? #{target.name}"
if (target.respond_to?(:headers_build_phase) && target.name.include?("React-bridging"))
puts "target has headers build phase, setting public attrs"
target.headers_build_phase.files.each do |file|
puts "setting attributes on header build phase #{file.file_ref.name}"
file.settings = { 'ATTRIBUTES' => ['Public'] }
end
end
#Fix search paths for React-bridging
installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
target_installation_result.native_target.build_configurations.each do |config|
# For third party modules who have React-bridging dependency to search correct headers
config.build_settings['HEADER_SEARCH_PATHS'] ||= '$(inherited) '
config.build_settings['HEADER_SEARCH_PATHS'] << '"$(PODS_ROOT)/Headers/Private/React-bridging" '
config.build_settings['HEADER_SEARCH_PATHS'] << '"$(PODS_CONFIGURATION_BUILD_DIR)/React-bridging/_.framework/Headers" '
end
end
# @generated end rn-firebase-use-frameworks-hacks-header-paths
react_native_post_install(installer)
__apply_Xcode_12_5_M1_post_install_workaround(installer)
# This is necessary for Xcode 14, because it signs resource bundles by default
# when building for devices.
installer.target_installation_results.pod_target_installation_results
.each do |pod_name, target_installation_result|
target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
resource_bundle_target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
end
end
end
end
Hello 👋, to help manage issues we automatically close stale issues.
This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?
This issue will be closed in 15 days if no further activity occurs.
Thank you for your contributions.
This is fixed in react-natiive 0.71, until then see here for a patch I made to backport the fixes to 0.70.6 https://github.com/nrwl/nx/issues/14014#issuecomment-1374497393
Hello 👋, to help manage issues we automatically close stale issues.
This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?
This issue will be closed in 15 days if no further activity occurs.
Thank you for your contributions.