Native View Presenting a React Native View

Does anyone know how I can have a native UIViewController present or push a React Native view, and go back and forth between the two?

If possible, I'd like to load the React Native view inside a UIViewController so I can retain my navigation bar, but use the React Native view.

I've been reading the documentation, but I have not been able to find anything that works.

I've tried a combination of this:

//  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:nil launchOptions:nil];
//  RCTRootView *reactView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"My Test" initialProperties:nil];

    NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
    RCTRootView *reactView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                         moduleName:@"My Test"
                                                  initialProperties:nil
                                                      launchOptions:nil];

    reactView.frame = self.view.frame;
    [self.view addSubview:reactView];

I'm not quite sure I understand how it knows what .js React file I want it to use, or how I specify that.

To have this kind of approach working you'll at least need:

  1. Access to a bridge that all your RCTRootViews can share. If you want each root view to load a different RN component from the same bundle - they need to share the sameRCTBridge
  2. A UIViewController that its view is a RCTRootView. You can then use this view controller as the root of a navigation controller, show it modally etc. like you've already tried to do.

Let's take a look at a very simplistic example implementation of this idea.

1. An object that holds the shared bridge

Header:

#import <Foundation/Foundation.h>
#import "RCTBridge.h"

@interface ReactBridgeManager : NSObject
@property (nonatomic, strong, readonly) RCTBridge *bridge;
-(instancetype)initWithBundleURL:(NSURL *)bundleURL launchOptions:(NSDictionary *)launchOptions;
@end

Implementation:

#import "ReactBridgeManager.h"

@interface ReactBridgeManager () <RCTBridgeDelegate>
@property (nonatomic, strong, readwrite) RCTBridge *bridge;
@property (nonatomic, strong) NSURL *bundleURL;
@end

@implementation ReactBridgeManager

- (instancetype)initWithBundleURL:(NSURL *)bundleURL launchOptions:(NSDictionary *)launchOptions
{
  self = [super init];
  if (self)
  {
    self.bundleURL = bundleURL;
    self.bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  }
  return self;
}

#pragma mark - RCTBridgeDelegate methods

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
  return self.bundleURL;
}

@end

2. A view controller that controls a react root view

Header

#import <UIKit/UIKit.h>
#import "RCTBridge.h"

@interface ReactViewController : UIViewController
- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString*)moduleName initialProps:(NSDictionary*)initialProps;
@end

Implementation

#import "ReactViewController.h"
#import "RCTRootView.h"

@implementation ReactViewController

- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString*)moduleName initialProps:(NSDictionary*)initialProps
{
  self = [super init];
  if(self)
  {
    self.view = [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProps];
  }
  return self;
}

@end

3. Usage example

You can now create instances of ReactViewController where necessary with your react component name (this is a component that was registered in RN AppRegistry on the JS side) and push it into your native navigation stack, show it as a modal, etc.

//when your app is initialized, create the bridge manager and hold a reference to it.
ReactBridgeManager *bridgeManager = [[ReactBridgeManager alloc] initWithBundleURL:jsCodeLocation launchOptions:launchOptions];

//create a view controller that's controlling a react root view
ReactViewController *reactViewController = [[ReactViewController alloc] initWithBridge:bridgeManager.bridge moduleName:@"MyComponent" initialProps:nil];

Taking it to the next level

You can take a look at React Native Navigation which basically uses the same concept to allow a fully native navigation solution for react-native apps. Disclaimer: I'm part of the team that develops this package :)

Comments

Popular posts from this blog

Meaning of `{}` for return expression

Get current scroll position of ScrollView in React Native

React Native - Image Cache