Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add custom request handlers on new arch #36071

Conversation

WoLewicki
Copy link
Contributor

Summary

Right now modules conforming to RCTURLRequestHandler protocol are hardcoded on new arch and there is no way to add your own modules there. I added the first look on a method providing all such modules there. The work is connected with bringing new arch to react-native-cameraroll: react-native-cameraroll/react-native-cameraroll#460. For now I didn't convert the RNCAssetsLibraryRequestHandler from that lib to TurboModule since there is no way to get it then.

Changelog

[IOS] [ADDED] - custom RCTURLRequestHandler modules on new arch

Test Plan

Try and register a custom module conforming to RCTURLRequestHandler on new arch.

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Feb 6, 2023
@analysis-bot
Copy link

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 8,474,299 +0
android hermes armeabi-v7a 7,795,951 +0
android hermes x86 8,950,182 +0
android hermes x86_64 8,807,846 +0
android jsc arm64-v8a 9,656,807 +0
android jsc armeabi-v7a 8,392,045 +0
android jsc x86 9,720,418 +0
android jsc x86_64 10,197,080 +0

Base commit: 34604fa
Branch: main

[modules addObjectsFromArray:[bridge modulesConformingToProtocol:protocol]];
}

// TODO: find a way to get the same information of turboModules
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the problem here is that all the TM are lazily loaded. The system does not know at startup which modules are TM and which are not. So, it's not possible to query the TurboModule registry to check which modules have been registered.

One way could be to be explicit and provide a method to list the handlers that the user has to install. However this will pollute the user space that now has suddenly to do an extra thing to properly configure its app.

Another way could be to rely on Codegen, we can introduce a default class that is generated which provides the URLRequestHandlers for the turbomodules that are marked is some specific way.

Alternatively, we could try to see what the CLI does when it autolinks and add a new step there.

This is probably the best approach as we could make it efficient and transparent.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both approaches sound good and I don't have enough knowledge of the internals to be able to say more 😅 Anything that works and does not require the user of the library to do anything is ok for me.

Copy link
Contributor

@RSNara RSNara Feb 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there shouldn't be a need to introduce this modulesConformingToProtocol API in the TurboModule system for this use-case. The application knows exactly which modules are registered with it (including all the ones it gets from libraries). So, the application should be able to create and return an RCTNetworking module with all the RCTURLRequestHandlers in that application.

The work is connected with bringing new arch to react-native-cameraroll: react-native-cameraroll/react-native-cameraroll#460

@WoLewicki, could you describe your problem in more details? Is the problem that React Native Camera Roll uses React/AppSetup/RCTAppSetupUtils.mm to create its modules?

@cipolleschi, I think we should definitely re-think this utility:

id<RCTTurboModule> RCTAppSetupDefaultModuleFromClass(Class moduleClass)
{
// Set up the default RCTImageLoader and RCTNetworking modules.
if (moduleClass == RCTImageLoader.class) {
return [[moduleClass alloc] initWithRedirectDelegate:nil
loadersProvider:^NSArray<id<RCTImageURLLoader>> *(RCTModuleRegistry *moduleRegistry) {
return @[ [RCTLocalAssetImageLoader new] ];
}
decodersProvider:^NSArray<id<RCTImageDataDecoder>> *(RCTModuleRegistry *moduleRegistry) {
return @[ [RCTGIFImageDecoder new] ];
}];
} else if (moduleClass == RCTNetworking.class) {
return [[moduleClass alloc]
initWithHandlersProvider:^NSArray<id<RCTURLRequestHandler>> *(RCTModuleRegistry *moduleRegistry) {
NSArray *handlers = [moduleRegistry modulesConformingToProtocol:@protocol(RCTURLRequestHandler)];
NSArray *handlersWithDefaultOnes = [handlers arrayByAddingObjectsFromArray: @[
[RCTHTTPRequestHandler new],
[RCTDataRequestHandler new],
[RCTFileRequestHandler new],
]];
return handlersWithDefaultOnes;
}];
}
// No custom initializer here.
return [moduleClass new];
}

It locks people into pre-customized networking and image loader modules.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RSNara yes, I think that we should step back and have a look at how we register components and turbomodules in the users apps as we cannot ask them to manually register those elements. Right now we are working around it using the CLI.

(as a side note: those methods where there before I joined. I don't have context as why they are the way they are. 😅 )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WoLewicki, could you describe your problem in more details? Is the problem that React Native Camera Roll uses React/AppSetup/RCTAppSetupUtils.mm to create its modules?

@RSNara I am only porting the cameraroll to new arch so I don't have too deep knowledge, but it seems that the module just wants to handle iOS photos in requests and it seems to have been done by implementing RCTURLRequestHandler protocol on Paper in order for the module to be taken into account when parsing requests in RCTNetworking.mm. It does not work on new arch when module being converted to turboModule though.

@facebook-github-bot facebook-github-bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 20, 2023
Copy link

This PR is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@github-actions github-actions bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Jan 17, 2024
@WoLewicki
Copy link
Contributor Author

cc @cipolleschi @RSNara did you come to a conclusion of how should this be handled?

@github-actions github-actions bot removed the Stale There has been a lack of activity on this issue and it may be closed soon. label Jan 18, 2024
@cipolleschi
Copy link
Contributor

Same as per the other PR, I created an internal tasks and we are going to discuss it on Monday or Wednesday.
Thanks for the patience.

@cipolleschi
Copy link
Contributor

Hi @WoLewicki, sorry for the long wait.

To sumup the discussion:

  • we can't land this PR as decribed here. The problem is that this will not work internally and iterating over all the current modules will introduce several perf issue.
  • the ideal solution should have a static or a build-time representation of the modules that can be added as custom URL handlers
  • the ideal solution should work with bridgeless too.

One way to achieve this that I was thinking about follow roughly these lines:

  1. we add two methods to the RCTTurboModuleManagerDelegate:
    // Returns a list of classes that conform to `RCTURLRequestHandler`
    - (NSArray<NSString * > *)customURLHandlerClassNames;
  2. We add a new property to the codegenConfig field, for library only:
    {
        codegenConfig : {
          ...codegenConfig,
          ios: {
            urlHandlers: ['list',  'of', 'NativeModules', 'that', 'implements', 'RCTURLRequestHandler']
        }
    }
  3. We update the RCTAppDelegateUtils to call the two new methods of the RCTTurboModuleManagerDelegate so that:
    id<RCTTurboModule> RCTAppSetupDefaultModuleFromClass(Class moduleClass)
    {
      //...
      } else if (moduleClass == RCTNetworking.class) {
        return [[moduleClass alloc]
            initWithHandlersProvider:^NSArray<id<RCTURLRequestHandler>> *(RCTModuleRegistry *moduleRegistry) {
              NSMutableArray customURLRequestHandlers = [NSMutableArray new];
              
              // Known objects
              [customURLRequestHandlers addObject: [RCTHTTPRequestHandler new]];
              [customURLRequestHandlers addObject: [RCTDataRequestHandler new]];
              [customURLRequestHandlers addObject: [RCTFileRequestHandler new]];
              [customURLRequestHandlers addObject: [moduleRegistry moduleForName:"BlobModule"]];
              
              // Objects from NativeModules that can handle urls
              NSArray<NSString *> * classString = [self customURLHandlerNativeModules];
              for (NSString * nativeModule in classString) {
                id urlHandlerModule = [moduleRegistry moduleForName:"BlobModule"];
                if (urlHandlerModule != NULL) {
                  [customURLRequestHandlers addObject:[moduleRegistry moduleForName:"BlobModule"]];
                } else {
                   class clazz = NSClassFromString(urlHandlerClass);
                   if (clazz != NULL) {
                     [customURLRequestHandlers addObject:[clazz new]];
                   }
                }
              }
            
              return customURLRequestHandlers;
            }];
      }
      // No custom initializer here.
      return [moduleClass new];
    }
  4. We update codegen to create the lists of customURLHandlerClassNames.

@RSNara @WoLewicki what do you think about the approach?
In this way, library just have to add a single line in their codegen definition to instruct the ReactNative app to generate the autolinking code properly.

@WoLewicki
Copy link
Contributor Author

LGTM, it adds some complexity, but there are probably very little libraries that would like to do it so I'm ok with it 🚀

@cipolleschi
Copy link
Contributor

On the library side, it would be just to specify an extra line in the codegenConfig field, or am I missing something?

@WoLewicki
Copy link
Contributor Author

Oh ok, then it seems like a perfect option.

@cipolleschi
Copy link
Contributor

Closing this as commit 4236538 landed and it implements a mechanism to link custom modules that conforms to some specific React Native protocols.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants