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

🔥 [🐛] iOS build fails with use_frameworks! and pnpm due to React-bridging header path issue

Open evelant opened this issue 3 years ago • 6 comments

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

  1. npm install -g pnpm
  2. npm install -g eas-cli
  3. pnpm install
  4. 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

  1. metro.config.js uses @rnx-kit/metro-resolver-symlinks to work around lack of symlink support in metro
  2. .npmrc contains a couple public-hoist-pattern entries to ensure some undeclared dependencies are resolvable in node_modules.
  3. 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

  1. patches/[email protected] fixes the header path of React-bridging per react-native #34102
  2. update_ios_podfile_plugin.js modifies the Podfile to 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.settings jetifier=true for Android compatibility?
  • [ ] I am using the NPM package jetifier for 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-firebase version you're using that has this issue:
    • N/A -- issue is with use_frameworks!
  • Firebase module(s) you're using that has the issue:
    • e.g. Instance ID
  • Are you using TypeScript?
    • Y - 4.8.3

evelant avatar Sep 21 '22 12:09 evelant

While this is a most excellent write up - it really is, I just don't see how this is actionable here?

mikehardy avatar Sep 21 '22 12:09 mikehardy

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.

evelant avatar Sep 21 '22 12:09 evelant

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

mikehardy avatar Sep 21 '22 12:09 mikehardy

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 avatar Sep 21 '22 12:09 atanaskanchev

@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

mikehardy avatar Sep 21 '22 13:09 mikehardy

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

evelant avatar Sep 21 '22 13:09 evelant

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.

Screenshot 2022-11-27 at 4 17 57 PM

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

econnerty avatar Nov 27 '22 21:11 econnerty

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.

github-actions[bot] avatar Dec 25 '22 21:12 github-actions[bot]

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

evelant avatar Jan 07 '23 14:01 evelant

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.

github-actions[bot] avatar Feb 04 '23 14:02 github-actions[bot]