React Native Bridge For iOS

React Native is developed in such a way that we can create a bridge between the Native Language and the JavaScript code. A bridge is nothing but a way to set up communication between native platforms and React Native.

But why do we need it?

Let’s assume you want to reuse some existing Objective-C/ Swift code or library without having to reimplement it in JavaScript.

Yes, you guessed it right!

You can use it in your React Native application with the help of Native Bridge and at some point in time, to make a production-level application you will most probably use Native Bridge.

As long as we are building our application purely within the framework, we can drive our app with properties and callbacks. But, when we mix React Native and native components, we need some specific cross-language mechanisms that would allow us to pass information between them.

Source: official react native docs

What we will learn in this article?

We will separate this article into two parts:

  • Native Modules
  • Native UI Components

Native Modules

A Native Module is just a set of javascript functions that are implemented natively for the individual platform. Native Modules are used when React Native doesn’t have that required module yet, or when the native performance is significantly better.

To understand this concept better, we will define some functions in Swift which will control the status of a torch (whether it is “on” or “off”) and we will call these functions from React Native.

Let’s start:

Before you start you should have Xcode and Node.js installed on your computer.

Open your terminal and follow along:

npx react-native init ReactNativeBridgeIos

cd ReactNativeBridgeIos

  • Now- Open ReactNativeBridgeIos.xcodeproj file in the iOS folder (It would automatically open your iOS code in Xcode).
  • Now- Create a Swift file torch.swift in the ReactNativeBridgeIos folder. It will also ask for creating a Bridge Header file- accept that- a file named ReactNativeBridgeIos-Bridging-Header.h will be created.
  • Now- Add the following line in the ReactNativeBridgeIos-Bridging-Header.h file


#import "React/RCTBridgeModule.h"

RCTBridgeModule will just provide an interface to register a bridge module where RCT is just an abbreviation of ReaCT.

Now- Open ‘Torch.swift’ and add the following code:


import Foundation
@objc(Torch) class Torch: NSObject {
  @objc static func requiresMainQueueSetup() -> Bool {return true}
  
  @objc static var isOn = false
  
  @objc func turnOn() {
    Torch.isOn = true
  }
  @objc func turnOff() {
    Torch.isOn = false
  }
  @objc func getTorchStatus(_ callback: RCTResponseSenderBlock) {
    callback([NSNull(), Torch.isOn])
  }
}

A Torch class is created which is inherited from NSObject.

NSObject: The root class of most Objective-C class hierarchies, from which subclasses inherit a basic interface to the runtime system and the ability to behave as Objective-C objects.”

source: developer.apple.com

A static variable “isOn” is created to set the status of the torch which will accept boolean values. Two functions “turnOn” and turnOff are created just to change the status of our torch. And a getTorchStatus function, which will accept a callback parameter to get the status of the torch which we will call from javascript.

RCTResponseSenderBlock” accepts only one argument – an array of parameters to pass to the JavaScript callback. And, NSNull() is passed as the first element which we have considered as an error in the callback.

A function requiresMainQueueSetup is made to make this code run on MainQueue. Else, it will give a warning about the same.

You may have already noticed that we have used “@objc” before the class and the functions (this is just to make the class, functions, or variables available to Objective-C).

Now- Create an Objective-C file named Torch.m by pressing cmd + N and add the following code:


#import <Foundation/Foundation.h>
#import "React/RCTBridgeModule.h"
@interface RCT_EXTERN_MODULE(Torch,NSObject)
RCT_EXTERN_METHOD(turnOn)
RCT_EXTERN_METHOD(turnOff)
RCT_EXTERN_METHOD(getTorchStatus: (RCTResponseSenderBlock)callback)
@end

To expose the functions of native to react javascript, we have to do that explicitly by using RCT_EXTERN_METHOD()

Now- To access the torch class from javascript, we have to import {NativeModules} from ‘react-native’. add the following code in the App.js file.


import React, {Component} from 'react';
import {StyleSheet, Text, View, NativeModules, Button} from 'react-native';
const {Torch} = NativeModules;
export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isOn: false,
    };
    this.updateTorchStatus();
  }
  turnOn = () => {
    Torch.turnOn();
    this.updateTorchStatus();
  };
  turnOff = () => {
    Torch.turnOff();
    this.updateTorchStatus();
  };
  updateTorchStatus = () => {
    Torch.getTorchStatus((error, isOn) => {
      this.setState({isOn: isOn});
    });
  };
  render() {
    return (
      <View style={styles.container}>
        <Text style={{fontSize: 24}}>
          Torch is {this.state.isOn ? 'ON' : 'OFF'}
        </Text>
        {!this.state.isOn ? (
          <Button onPress={this.turnOn} title="Switch ON " color="green" />
        ) : (
          <Button onPress={this.turnOff} title="Switch OFF " color="red" />
        )}
      </View>
    );
  }
}
const styles = StyleSheet.create({
  container: {flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#d3d3d3',},
});

In the above code, the state of the torch is set by the updateTorchStatus function which is using the Native function getTorchStatus and passing a callback to it. A button is made which on pressing toggle the status of the torch in the Native code.

Now, you can start the application by running the following command:

npx react-native run-ios

You will see something like this:

After clicking the button:

So, you can see that we have created a Native Bridge for iOS.

So this is all about Native Modules, now let’s head over to the second part of this article which is Native UI Components.

Native UI Components

There are tons of native UI widgets out there ready to be used in the latest apps. Fortunately, we can wrap up these existing components for seamless integration with our React Native application.

Without further ado, let’s head over to the practical part.

First, Let’s create a simple SwitchApp example using react-native CLI.

npx react-native init SwitchApp

cd SwitchApp

Let’s create a Switch UI component:

  • For that open SwitchApp.xcodeproj file (from iOS folder, in Xcode just like we did in the Native Module section).
  • Then, create a SwitchView.swift file from

file (from tab bar)> new > file > Cocoa Touch Class

and name that SwitchView and also accept the “Create Bridging Header” when prompted, which will create a file SwitchApp-Bridging-Header.h. This will help to communicate between Swift and Objective C code.

Now, add the following import statements in SwitchApp-Bridging-Header.h file.


#import "React/RCTBridgeModule.h"
#import "React/RCTViewManager.h"
#import "React/RCTEventEmitter.h"

RCTBridgeModule will just provide an interface to register a bridge module where RCT is just an abbreviation of ReaCT.

Now, open ‘SwitchView.swift’ and add the following code:


import UIKit
class SwitchView: UIView {
  
  @objc var isOn: Bool = false  {
    didSet {
          button.backgroundColor = isOn ? .yellow : .black
          button.setTitle(String(describing: isOn ? "I am ON" : "I am OFF"), for: .normal)
     }
  }
  override init(frame: CGRect) {
    super.init(frame: frame)
    self.addSubview(button)
  }
  required init?(coder aDecoder: NSCoder) {
    fatalError("init has not been implemented")
  }
  lazy var button: UIButton = {
      let button = UIButton.init(type: UIButton.ButtonType.system)
      button.autoresizingMask = [.flexibleWidth, .flexibleHeight]
      button.titleLabel?.font = UIFont.systemFont(ofSize: 20)
      button.addTarget(
        self,
        action: #selector(toggleSwitchStatus),
        for: .touchUpInside
      )
      return button
    }()
  @objc func toggleSwitchStatus() {
    isOn = !isOn as Bool
  }
}

In the above code: We created a Switch View which is inherited from UIView. A boolean variable “isOn” is defined for the status of the Switch whether it is on or off. Also, color, title, size, etc. are defined.

On clicking the button the “toggleSwitchStatus” method is called, which does nothing but toggle the value of “isOn” variable to true or false.

Now, create a Switch.swift file and add the following code:


import Foundation
@objc(Switch)
class Switch: RCTViewManager {
  
  override func view() -> UIView! {
    return SwitchView()
  }
  
  override static func requiresMainQueueSetup() -> Bool {
    return true
  }
}

A Switch class inherited from RCTViewManager is created.

“Native views are created and manipulated by subclasses of RCTViewManager. These subclasses are similar in function to view controllers, but are essentially singletons – only one instance of each is created by the bridge. They expose native views to the RCTUIManager, which delegates back to them to set and update the properties of the views as necessary.”

Source: official react-native docs

A function “requiresMainQueueSetup” is made to make this code run on MainQueue. Else, it will give a warning about the same.

Now- Create an Objective-C file with the name of Switch.m and add the following code:


#import "React/RCTViewManager.h"
@interface RCT_EXTERN_MODULE(Switch, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(isOn  , BOOL)
@end

To expose the functions of native to react javascript- we have to do that explicitly by using RCT_EXTERN_MODULE(). By that, we have exposed Switch class and properties to our javascript code.

Now, to use the Switch UI button in javascript we have to import {requireNativeComponent} from ‘react-native’,

Add the following code in the App.js file


import React, {Component} from 'react';
import {StyleSheet, View, requireNativeComponent, Text} from 'react-native';
const Switch = requireNativeComponent('Switch');
export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Switch style={styles.nativeBtn} isOn={false} />
      </View>
    );
  }
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'pink',
    alignItems: 'center',
    justifyContent: 'center',
  },
  nativeBtn: {
    height: 100,
    width: 300,
    backgroundColor: 'yellow',
  },
});

The requireNativeComponent function takes the name of the native view. In this case, we used the name “Switch” and now <Switch> component is ready to be used.

Now we have a button which is made in Swift and shown in the app through javascript.

Now, run the app by-

npx react-native run-ios

You will be seeing the button like this:

After clicking on it:

In this article, we have created both the Native Modules and Native UI components and use them through the react-native javascript code.
For react native bridge for android, refer this article https://codersera.com/blog/react-native-bridge-for-android/


Frequently asked questions

What is RCTBridgeModule?

RCTBridgeModule just provides an interface to register a bridge module where RCT is just an abbreviation of ReaCT.

Is react native slow?

In short, the answer is ‘yes’ as the react code and the native code needs to communicate all the time. Sometimes when react native seems slow, its due to the unnecessary re-renders happening behind the scenes which can be avoided by using lifecycle hooks.

Is react native free?

Yes React Native is free as it is an open-source platform. That means all documentation related to this technology is open for everyone and is available for free to everyone in React Native community. 

How many threads does react native have?

There are mainly 3 threads in React Native App:
1) UI thread: Also known as Main Thread. This is used for native android or iOS UI rendering.
2) JS thread: Javascript thread is the thread where the logic will run.
3) Native Modules Thread: Sometimes an app needs access to platform API, and this happens as part of native module thread.

Leave a Comment

Your email address will not be published. Required fields are marked *