Skip to content

Ability to skip autolinking for dependency for specific ios target #1179

Closed

Description

@anta-semenov

Describe the Feature

We use the same codebase for different apps in appstore, so we have different targets in pod file. For some of the apps we need to remove specific dependencies (for instance, we can't use firebase admob library in target that is aiming for kids due to appstore policy).

Currently in react-native.config.js I can disable autolinking only for specific platfrom, but it would be good to be able to skip dependency for specific pod targets as well

Possible Implementations

I can imagine that it could be a specific property in react-native.config.jsin dependency configuration where I can putt targets that should be excluded from autolinking

module.exports = {
  assets: ['res/fonts'],
  dependencies: {
    'react-native-code-push': {
      platforms: {
        ios: {
          excludeFor: ['Target1'],
        },
        android: null,
        web: null,
      }
    }
  }
}

I think in ../node_modules/@react-native-community/cli-platform-ios/native_modules it could be possible to get this property for dependency and skip installation if current target is listed

Activity

  1. thymikee commented on May 27, 2020

    @thymikee
    Member
  2. alloy commented on May 27, 2020

    @alloy
    Member

    In my opinion, which is very strong when it comes to options/configuration 😅, auto-linking should be as simple as it can be without the need for configuration. Any further customisation should be done entirely through normal CocoaPods [or Gradle] means.

    The reason I feel this way is that I think it ends up hurting the user if we try to abstract away things that are already well covered by the amount of docs/issues/stackoverflow/blogs that exist for the underlying tool–in this case CocoaPods. Instead we would have to write and maintain new rn-cli specific documentation, meaning maintainers need to do more work and end-users have less chance of finding the right solution; which is a lose-lose situation.

    In short, I think auto-linking should:

    1. not ever try include dependencies on platforms the pod doesn’t support (added in feat(cli-platform-ios): add macOS auto-linking support #1126)
    2. auto-linking should skip pods that were already explicitly activated by the user prior to invoking auto-linking, thus allowing the user to provide custom configuration for that pod (already works this way)
    3. auto-linking can be invoked from within specific targets, thus not affecting other targets (should already work this way)

    Thus, I don’t think we should add this feature, furthermore I believe we should remove the ability to exclude pods for specific platforms. It would also be good to outline the above design in the auto-linking doc.

  3. alloy commented on May 27, 2020

    @alloy
    Member

    @anta-semenov So coming back to your original issue, I think your best recourse would be to follow the strategy outlined in point 3 above; which is to:

    • invoke use_native_modules! in the targets where you want auto-linking to do its work
    • manually define the pods in those targets where you want total control over them.

    If this doesn't work, then please let us know! 🙏

  4. anta-semenov commented on May 28, 2020

    @anta-semenov
    Author

    Not sure if this will work well with my user case.
    My use case is following (pod file):

    abstract_target 'Main' do
      <React native pods>
      <Common dependencies, around 20>
    
      target InAppAddAllowed do
        pod pod 'RNFBAdMob', :path => '../node_modules/@react-native-firebase/admob'
      end
      target AppWithoutAdd
    end
    

    I would like to use autolinking to install common dependencies, but according to my understanding if I add use_native_modules! into Main target level it will also add RNFBAdMob, because it will find it in node_modules (or maybe I'm wrong in my understanding how the whole thing work). I can use use_native_modules! only inside InAppAddAllowed to add one dependency which obviously doesn't have any sense.
    And it's not very easy to remove dependency from target with cocoapods (at least there is no docs around this), so I can't use cocopods hooks for that

    But I got your point. Will patch this locally.

    I have one question in how merging of react-native.config.js works.
    Is there any merging at all, meaning if I define dependencies in my local react-native.config.js and there is already some config in package, will it merge configs or will it select only one of them?
    And if it merges, will it merge only known properties or all of them?

    And in my proposal, I didn't mean that there should be the need for configuration, but ability for configuration

  5. alloy commented on May 28, 2020

    @alloy
    Member

    Can you check what this does?

    abstract_target 'Main' do
      <Common dependencies, around 20>
    
      target "InAppAddAllowed" do
        pod 'RNFBAdMob', :path => '../node_modules/@react-native-firebase/admob'
        use_native_modules!
      end
    
      target "AppWithoutAdd" do
        use_native_modules!
      end
    end

    If this does not already omit RNFBAdMob from the AppWithoutAdd target, then we should update it to do so, as that’s the way I would prefer configuration to be done.

    And in my proposal, I didn't mean that there should be the need for configuration, but ability for configuration

    To be clear, my first comment wasn’t necessarily addressed at you, more so to the other maintainers of this tool and for posterity.

    I think we are on the same page, we want to be able to control these things in as easy a way as possible.

  6. anta-semenov commented on May 29, 2020

    @anta-semenov
    Author

    Will try today or on monday. But according to code in @react-native-community/cli-platform-ios/native_modules.rb it checks dependencies only for current target, so inside AppWithoutAdd it most likely won't check dependencies of InAppAddAllowed (or maybe I completely don't understand how cocoapods works)

  7. Charlie-Hua commented on Jan 8, 2021

    @Charlie-Hua

    I ended up patching native_modules.rb to take packages_to_skip in use_native_modules!, so I can do this in my Podfile:

    abstract_target 'Main' do
      # common dependencies, around 20
      use_native_modules!(packages_to_skip: ['RNFBAdMob'])
    
      target "InAppAddAllowed" do
        pod 'RNFBAdMob', :path => '../node_modules/@react-native-firebase/admob'
      end
    
      target "AppWithoutAdd" do
      end
    end

    here is the content of my patches/@react-native-community+cli-platform-ios+4.13.0.patch file

    diff --git a/node_modules/@react-native-community/cli-platform-ios/native_modules.rb b/node_modules/@react-native-community/cli-platform-ios/native_modules.rb
    index 2254158..397ca0a 100644
    --- a/node_modules/@react-native-community/cli-platform-ios/native_modules.rb
    +++ b/node_modules/@react-native-community/cli-platform-ios/native_modules.rb
    @@ -12,7 +12,7 @@
     require 'pathname'
     require 'cocoapods'
     
    -def use_native_modules!(config = nil)
    +def use_native_modules!(config = nil, packages_to_skip: [])
       if (config.is_a? String)
         Pod::UI.warn("Passing custom root to use_native_modules! is deprecated.",
           [
    @@ -44,6 +44,10 @@ def use_native_modules!(config = nil)
     
       packages.each do |package_name, package|
         next unless package_config = package["platforms"]["ios"]
    +    if skipped_pod = packages_to_skip.find { |pod_name| package["platforms"]["ios"]["podspecPath"].include? "#{pod_name}.podspec" }
    +      Pod::UI.notice "Skipping pod: #{skipped_pod}"
    +      next
    +    end
     
         podspec_path = package_config["podspecPath"]
     
  8. stockhuman commented on Feb 24, 2022

    @stockhuman

    Hey @Charlie-Hua, that's a great idea. This should make it to the canonical implementation; as niche as the the feature may be, it's incredibly useful for simulator vs real-device-only pods & separation of concerns. We use to it to split out our Unity framework interop. Thanks!

  9. tapi commented on Mar 10, 2022

    @tapi

    The skip list is exactly what we need. We have a dependency required only for Android but it has an iOS native module that we don't want. Generally I agree with @alloy, configuration should be easy, but I don't think an exclusion list is at odds with that.

  10. pot2mayo commented on Mar 14, 2022

    @pot2mayo

    Note that this feature would also be extremely useful when creating an iOS app clip!
    App clips have strong size constraints (10MB uncompressed), therefore being able to filter out packages that you don't need in the clip (which must have its own target in the Podfile) would be great since it cannot be done in the package.json which is shared between the app and the clip.

    For now I'm gonna try @Charlie-Hua's solution which is a very nice workaround this limitation 👍🏼

  11. Ross-Landry commented on Oct 27, 2022

    @Ross-Landry

    @pot2mayo Did that work for you?

    I am trying to reduce my app clip size as well. I tried the work-around but I did not see a reduction in size. (I also didn't see any messages in the console about pods being skipped, so I'm not sure that my attempt to copy the patch worked.) Curious if you had success in this.

  12. github-actions commented on Jan 26, 2023

    @github-actions

    There hasn't been any activity on this issue in the past 3 months, so it has been marked as stale and it will be closed automatically if no further activity occurs in the next 7 days.

  13. sa-ma commented on Feb 20, 2024

    @sa-ma

    @pot2mayo Did that work for you?

    I am trying to reduce my app clip size as well. I tried the work-around but I did not see a reduction in size. (I also didn't see any messages in the console about pods being skipped, so I'm not sure that my attempt to copy the patch worked.) Curious if you had success in this.

    @Ross-Landry
    Were you able to solve this?

  14. kecoco16 commented on May 30, 2024

    @kecoco16

    @sa-ma To reduce the App clip, you can use this approach. This way you can use the same base code for both the app and the app clip and only install the Pods necessary for the app clip and in this way not increase the size of the app clip, thanks to @dawidzawada for sharing this approach 💪💪

  15. ridvanaltun commented on Nov 27, 2024

    @ridvanaltun

    I patched react-native source code to manage packages over react-native.config.js file for app clips

    Android: https://gist.github.com/ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387
    iOS: https://gist.github.com/ridvanaltun/a02a9a8a8d4bbd354a5cd875a20cf21f

    @kecoco16 @sa-ma @pot2mayo

  16. c100k commented on Dec 13, 2024

    @c100k

    Should we re-open this, as discussions are still ongoing ?

  17. tux2nicolae commented on Jun 25, 2025

    @tux2nicolae

    We long lived with this #1179 (comment) workaround until now, it does not seem to work anymore because of components auto discovery (more in this issue) which are automatically included in the codegen.

    Did someone managed to get around it?

  18. mikebouwmans commented on Jul 19, 2025

    @mikebouwmans

    @tux2nicolae I was also getting that issue. I've fixed it using the following patch.

    Basically it replaces the NSClassFromString() with a safe call that doesn't add the component if the class is nil. This way it doesn't crash on runtime if a package is excluded with the fix @Charlie-Hua provided.

    Did not test this in prod yet.

    index dc0df1691edb5a0fefbb18be1b0466e34856ea71..9aaa314a66b9a70af923319a58c5d414c74262a6 100644
    --- a/scripts/codegen/generate-artifacts-executor/generateRCTThirdPartyComponents.js
    +++ b/scripts/codegen/generate-artifacts-executor/generateRCTThirdPartyComponents.js
    @@ -134,7 +134,7 @@ function generateRCTThirdPartyComponents(libraries, outputDir) {
         .flatMap(library => {
           const components = componentsInLibraries[library];
           return components.map(({componentName, className}) => {
    -        return `\t\t@"${componentName}": NSClassFromString(@"${className}"), // ${library}`;
    +        return `ADD_COMPONENT(@"${componentName}", @"${className}"); // ${library}`;
           });
         })
         .join('\n');
    diff --git a/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template b/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template
    index 4ce510709e337ce783a52413063b2c1ac6fcb29a..5024171da766539adb992542efe9234e6f54ff08 100644
    --- a/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template
    +++ b/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template
    @@ -19,9 +19,24 @@
       static dispatch_once_t nativeComponentsToken;
     
       dispatch_once(&nativeComponentsToken, ^{
    -    thirdPartyComponents = @{
    -{thirdPartyComponentsMapping}
    -    };
    +NSMutableDictionary<NSString *, Class<RCTComponentViewProtocol>> *components = [NSMutableDictionary new];
    +
    +
    +    #define ADD_COMPONENT(key, className) \
    +  do { \
    +    Class cls = NSClassFromString(className); \
    +    if (cls) { \
    +      components[key] = cls; \
    +    } else { \
    +      #if DEBUG
    +        NSLog(@"[Warning] Class '%@' not found for key '%@'", className, key); \
    +      #endif
    +    } \
    +  } while (0)
    +
    +  {thirdPartyComponentsMapping}
    +
    +  thirdPartyComponents = [components copy];
       });
     
       return thirdPartyComponents;
  19. tux2nicolae commented on Jul 19, 2025

    @tux2nicolae

    @mikebouwmans thanks for sharing, seems that this is the solution for now

    Yeap, I've made a similar one but forgot to post it here as well

    EDITED to react-native 82.1

    diff --git a/node_modules/react-native/scripts/cocoapods/autolinking.rb b/node_modules/react-native/scripts/cocoapods/autolinking.rb
    index d8e9c7c..136c940 100644
    --- a/node_modules/react-native/scripts/cocoapods/autolinking.rb
    +++ b/node_modules/react-native/scripts/cocoapods/autolinking.rb
    @@ -14,7 +14,7 @@ require_relative '../react_native_pods.rb'
     #
     # Parameters:
     # - config_command: the command to run to get the application's current config, e.g. ['npx', '@react-native-community/cli', 'config']
    -def list_native_modules!(config_command)
    +def list_native_modules!(config_command, packages_to_skip: [])
     
       if !(config_command.is_a? Array and config_command.size > 0)
         Pod::UI.warn "Expected a list_native_modules! to be called with a config command", [
    @@ -50,6 +50,10 @@ def list_native_modules!(config_command)
     
       packages.each do |package_name, package|
         next unless package_config = package["platforms"]["ios"]
    +    if skipped_pod = packages_to_skip.find { |pod_name| package["platforms"]["ios"]["podspecPath"].include? "#{pod_name}.podspec" }
    +      Pod::UI.notice "Skipping pod: #{skipped_pod}"
    +      next
    +    end
     
         name = package["name"]
         podspec_path = package_config["podspecPath"]
    @@ -207,6 +211,6 @@ $default_command = ['node', '-e', "process.argv=['', '', 'config'];require('@rea
     # Parameters:
     # - config_command: the command to run to get the application's current config, e.g. ['npx', '@react-native-community/cli', 'config'],
     #                   you can override this if you'd like to avoid the dependency. e.g. ['cat', 'your_config.json']
    -def use_native_modules!(config_command = $default_command)
    -  return link_native_modules!(list_native_modules!(config_command))
    +def use_native_modules!(config_command = $default_command, packages_to_skip: [])
    +  return link_native_modules!(list_native_modules!(config_command, packages_to_skip: packages_to_skip))
     end
    diff --git a/node_modules/react-native/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template b/node_modules/react-native/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template
    index 4ce5107..4206416 100644
    --- a/node_modules/react-native/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template
    +++ b/node_modules/react-native/scripts/codegen/templates/RCTThirdPartyComponentsProviderMM.template
    @@ -11,6 +11,15 @@
     #import "RCTThirdPartyComponentsProvider.h"
     #import <React/RCTComponentViewProtocol.h>
     
    +// Workaround to avoid nils in literal
    +#import <objc/runtime.h>
    +
    +#undef NSClassFromString
    +#define NSClassFromString(name) ({              \
    +  Class _cls = objc_getClass([name UTF8String]);\
    +  _cls ? _cls : (id)[NSNull null];              \
    +})
    +
     @implementation RCTThirdPartyComponentsProvider
     
     + (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
    @@ -19,9 +28,20 @@
       static dispatch_once_t nativeComponentsToken;
     
       dispatch_once(&nativeComponentsToken, ^{
    -    thirdPartyComponents = @{
    +    NSDictionary<NSString *, id> *rawMap = @{
     {thirdPartyComponentsMapping}
         };
    +
    +    NSMutableDictionary<NSString *, Class<RCTComponentViewProtocol>> *validComponents = [NSMutableDictionary new];
    +    [rawMap enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
    +      if ([obj isKindOfClass:[NSNull class]]) {
    +        NSLog(@"[RCTThirdPartyComponentsProvider] ⚠️ Component \"%@\" not found", key);
    +      } else {
    +        validComponents[key] = (Class<RCTComponentViewProtocol>)obj;
    +      }
    +    }];
    +
    +    thirdPartyComponents = [validComponents copy];
       });
     
       return thirdPartyComponents;
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions