React Native 模块中的依赖注入

问题描述 投票:0回答:3

我们正在将我的应用程序逐步转换为 React Native。 我一直遇到 iOS 上 React Native 中的依赖注入问题。

我的应用程序中有一些服务我想在本机模块中使用。目前,它们是通过 Typhoon 注入的,一切正常。

但是,React Native 本身会将任何本机模块初始化并维护为单例。这阻止我让 Typhoon 初始化它们,因此我无法将依赖项注入到它们中。

可以做什么?自己创建 RCTBridge 是一种选择,但感觉非常低级,并且仍然需要首先弄清楚如何将其注入到 UIView 中。

ios dependency-injection react-native
3个回答
17
投票

我不太清楚为什么你的问题没有得到更多的赞成票;我自己也在努力回答同样的问题,并认为我应该继续回答我的第一个 StackOverflow 问题!

挖掘 RCTBridge 类提供了答案。您需要做的是使用实现 RCTBridgeProtocol 的类的实例手动初始化 RCTBridge(重要的是“extraModulesForBridge”方法;如果您愿意,您甚至可以在视图控制器中实现此协议。

// Initialise a class that implements RCTBridgeDelegate
// Be warned, RCTBridge won't store a strong reference to your delegate.
// You should there store this delegate as a property of your initialising class, or implement the protocol in the View Controller itself. 
id<RCTBridgeDelegate> moduleInitialiser = [[classThatImplementsRCTBridgeDelegate alloc] init];

// Create a RCTBridge instance 
// (you may store this in a static context if you wish to use with other RCTViews you might initialise.  
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:moduleInitialiser launchOptions:nil];

// Initialise an RCTRootView
RCTRootView *rootView = [[RCTRootView alloc]
                     initWithBridge:bridge
                         moduleName:kModuleName
                  initialProperties:nil];

// Note that your class that implements RCTBridgeDelegate SHOULD implement the following methods and it might look something like this.

// This will return the location of our Bundle
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
    return [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
}

// Importantly, this is the method we need to implement to answer this question
// We must return an array of initialised Modules
// Be warned, I am writing this off of the top of my head, so excuse any syntax errors there might be!
- (NSArray *)extraModulesForBridge:(RCTBridge *)bridge
{
   return @[[OurNativeModule alloc] initWithCustomInitialiser:customDependency]];
}

编辑:我将其添加到 React-native 文档中。 https://facebook.github.io/react-native/docs/native-modules-ios.html#dependency-injection


7
投票

上面的代码运行得很好。这是 Swift 4 版本的代码。

@objc(RNModuleInitialiser)
final class RNModuleInitialiser: NSObject {

    //Inject your dependencies here
    init() {

    }

}

extension RNModuleInitialiser: RCTBridgeDelegate {

    func sourceURL(for bridge: RCTBridge!) -> URL! {
        return URL(string: "http://localhost:8081/index.ios.bundle?platform=ios")!
    }

    func extraModules(for bridge: RCTBridge!) -> [RCTBridgeModule]! {

        var extraModules = [RCTBridgeModule]()

        //Initialise the modules here using the dependencies injected above

        return extraModules
    }

}

初始化bridge时,传入moduleInitialiser:

//Make sure to hold the strong reference to the moduleInitaliser

self.moduleInitialiser = RNModuleInitialiser()
self.bridge = RCTBridge(delegate: self.moduleInitialiser, launchOptions: nil)

0
投票

这是我用来配置由react-native-bob-builder生成的外部本机模块并允许向其注入依赖项的示例。

React-native 文档对于 iOS 的手动链接似乎不完整,所以我实际上并没有 100% 遵循它,相反,我根据我发现的其他一些存储库创建了一个解决方案。

  • 添加了react-native-config.js以禁用iOS外部库的自动链接。
module.exports = {
  dependencies: {
    ['myLibJSPackageName']: {
      platforms: {
        android: null,
        ios: null, // Disable ios linking
        // Add more platforms to disable auto-linking for them too
      },
    },
  },
};
  • 将 lib pod 添加到目标
target 'MyLibExample' do
  pod 'myLibNameInsidePodSpec', :path => '../node_modules/myLibJSPackageName' '../..' If this is inside the example app generated by react-native-bob-builder  instead the path would be `:path => '../..'`
  ...
end
  • 使用接受参数的初始化构造函数创建了一个 .h 标头
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface MyLibName : RCTEventEmitter <RCTBridgeModule>

- (instancetype)initWithParam1:(NSString *)param1 param2:(NSString *)param2;

@end
  • extraModulesForBridge
    内通过
    AppDelegate.m
  • 添加手动初始化
#import <myLibNameInsidePodSpec/MyLibName.h> 

static NSString *const moduleName = @"MyLibExample";

...

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  self.moduleName = moduleName;

  // Start - Initialize bridge manually
  // Create a RCTBridge instance with the extraModulesForBridge delegate being the AppDelegate itself
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:nil];

  RCTRootView *rootView = [[RCTRootView alloc]
                      initWithBridge:bridge
                          moduleName:moduleName
                    initialProperties:nil];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  // End - Initialize bridge manually

  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

// Add this, if does not exists yet.
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
  NSMutableArray<id<RCTBridgeModule>> *extraModules = [NSMutableArray array];

  // Here the lib is initialized with custom params
  MyLibName *instance = [[MyLibName alloc] initWithParam1:@"value1" param2:@"value2"];
  [extraModules addObject:instance];

  return extraModules;
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
  #if DEBUG
    return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
  #else
    return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
  #endif
}

...

@end

如果您找到更好的解决方案,请随时更改。

© www.soinside.com 2019 - 2024. All rights reserved.