Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React-Native iOS - How can I navigate to a non-React-Native view (native iOS view controller) from a React-Native view with a button press?

The RN doco and other examples show how to launch a React-Native view from a native iOS view controller, but not the other way around. Can someone explain how I can do this?

like image 330
MStrapko Avatar asked Aug 17 '17 17:08

MStrapko


People also ask

Can you combine the native iOS and native Android code in React Native?

Yes, we can combine Android or iOS code in react native. React Native helps to smoothly combines the components written in Java, Objective-C or Swift.

How do you call native function from React Native?

To call a function, you can either pass its name and arguments to User. callFunction() or call the function as if it was a method on the User. functions property.


1 Answers

I was able to figure this out. In my case, I am using an Obj-C base project (which is the RN default) with my own Swift native view controller. My solution is here in case this comes up for anyone else:

Simply put, the answer is to use an RCTBridge module to allow the RN JavaScript to call a native iOS method.

Here is an outline of the components, followed by the implementation:

  1. AppDelegate.h/.m - Initialize the RN JavaScript index file for the initial RN view, also setup a method to swap the root view controller to a native view controller (this method will be called from the RTCBridge module.

  2. MyViewController.swift - A normal UIViewController with a standard implementation.

  3. MyProject-Bridging-Header.h - provides Obj-C <-> Swift communication

  4. ChangeViewBridge.h/.m - This provides the binding to allow you to call native iOS methods from the RN JavaScript.

  5. index.ios.js - Initialize your custom RCTBridge module and call the bound method to switch to your native view with a button press.

AppDelegate.h:

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate> {
  NSDictionary *options;
  UIViewController *viewController;
}

@property (nonatomic, strong) UIWindow *window;

- (void) setInitialViewController;
- (void) goToRegisterView; // called from the RCTBridge module

@end

AppDelegate.m:

#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "FidoTestProject-Swift.h" // Xcode generated import to reference MyViewController.swift from Obj-C

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  options = launchOptions;
  [self setInitialViewController];
  return YES;
}

- (void) setInitialViewController {
  NSURL *jsCodeLocation;

  jsCodeLocation = [NSURL URLWithString:@"http://192.168.208.152:8081/index.ios.bundle?platform=ios&dev=true"];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"FidoTestProject" initialProperties:nil launchOptions:options];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;

  viewController = rootViewController;

  [self.window makeKeyAndVisible];
}

// this method will be called from the RCTBridge
- (void) goToNativeView {
  NSLog(@"RN binding - Native View - MyViewController.swift - Load From "main" storyboard);
  UIViewController *vc = [UIStoryboard storyboardWithName:@"main" bundle:nil].instantiateInitialViewController;
  self.window.rootViewController = vc;
}

@end

MyViewController.swift:

class RegisterViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        print("MyViewController loaded...")
        // standard view controller will load from RN
    }
}

MyProject-Bridging-Header.h:

@import Foundation;
@import UIKit;
@import CoreLocation;
@import AVFoundation;

#import "React/RCTBridge.h"
#import "React/RCTBridgeModule.h"
#import "React/RCTBundleURLProvider.h"
#import "React/RCTRootView.h"
#import "AppDelegate.h"

ChangeViewBridge.h:

#import <React/RCTBridgeModule.h>

@interface ChangeViewBridge : NSObject <RCTBridgeModule>

- (void) changeToNativeView;

@end

ChangeViewBridge.m:

#import "RegisterBridge.h"
#import "FidoTestProject-Swift.h"
#import "AppDelegate.h"

@implementation ChangeViewBridge

// reference "ChangeViewBridge" module in index.ios.js
RCT_EXPORT_MODULE(ChangeViewBridge);

RCT_EXPORT_METHOD(changeToNativeView) {
  NSLog(@"RN binding - Native View - Loading MyViewController.swift");
  AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
  [appDelegate goToNativeView];
}

@end

index.ios.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

'use strict';

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Alert,
  Text,
  View,
  NativeModules,
  TouchableHighlight
} from 'react-native';

export default class FidoTestProject extends Component {

  constructor(props) {
     super(props)
     this.done = false;
   }

    _changeView() {
      this.done = true;
      this.render();
      NativeModules.ChangeViewBridge.changeToNativeView();
    }

  render() {
    if (!this.done) {
      return (
        <View style={styles.container}>
          <TouchableHighlight onPress={() => this._changeView()}>
            <Text color="#336699">
              Press to Change to Native View
            </Text>
          </TouchableHighlight>
        </View>
      );
    } else {
      return (<View></View>);
    }
  }
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  }
});

AppRegistry.registerComponent('FidoTestProject', () => FidoTestProject);
like image 195
MStrapko Avatar answered Sep 21 '22 09:09

MStrapko