React Native is an Objective-C application framework that bridges JavaScript applications running in the Jscore JavaScript engine to iOS and Android native APIs.
In theory,you write your application logic in JSX and ES6/7 and transpile it to JavaScript,and the application framework loads all that as a bundle.
In practice,you will want to expose your own custom native code to your JavaScript application. You may want to provide access to 3rd party library APIs or iOS framework features that aren’t exposed (yet) by React Native.
React Native is written in Objective-C,but we can write modules in Swift and expose them to our applications. The documentation on the React Native site briefly talks about “Exporting Swift,” but is thin on the details of doing much of anything in Swift. In this article,we’ll do a deeper dive into interfacing Swift to JavaScript.
It is assumed you already have React Native and its prerequisites installed on your system. You can find the code used in this article in this GitHub repository:https://github.com/ModusCreateOrg/swift-modules-for-react-native.
Create a React Native project and open it in Xcode:
$ react-native init SwiftBridge && cd $ ls android/ index.iosjs node_modules indexandroidjs ios packagejson $ ls ios SwiftBridge xcodeproj SwiftBridgeTests $ open ios/xcodeproj
Click the run button in Xcode and see that the project builds and runs.
We’ll start by implementingthe CalendarManager sample code from the React Native docsand see that it works.
First,we need to add our Swift source file. Right click on SwiftBridge and select “New File…”:
Choose iOS and Swift File from the “Choose template” dialog:
Choose a filename for the file:
We copy the CalendarManager.swift example code from the React Native docs page and paste it into our CalendarManager.swift file:
// // CalendarManager.swift// SwiftBridge// Created by Michael Schwartz on 12/11/15.// Copyright © 2015 Facebook. All rights reserved. importFoundation // CalendarManager.swift @objc(CalendarManager) class:NSObject{ @objc func addEvent(nameString, location dateNSNumber->Void // Date is ready to use! } }
Unfortunately,we have to provide an Objective-C file that exposes our Swift to the React Native Objective-C framework. Create the file “CalendarManageBridge.m” by selecting “New File” as before and choose Objective-C File this time:
This time you will be presented with a “Choose options for your new file” dialog:
The file is added to your project.
Copy and paste the code from the React Native docs page to this file:
// CalendarManagerBridge.m#import// CalendarManagerBridge.m #import "RCTBridgeModule.h" @interface RCT_EXTERN_MODULE ( CalendarManager NSObject ) RCT_EXTERN_METHOD ( addEvent :( NSString *) name location location date NSNumber date @end
Finally,we edit the SwiftBridge-Bridging-Header.h file and copy the two lines from the React Native docs page there:
// Use this file to import your target's public headers that you would like to expose to Swift.// CalendarManager-Bridging-Header.h#import "RCTBridgeModule.h"
Click on the run button in Xcode again and the project should run. If not,you did something wrong in the above steps.
varReact= require(‘reactnative’); // after this line consoledirReact.NativeModules); // ← add this line
When we run the project from Xcode with this line,we will get a red screen error:
You should see a new window or tab in Chrome that looks something like this:
After hitting Command-Shift-J as the page suggests,you should see something like this:
Let’s implement some code in the addEvent() method to see that we can call it from JavaScript and access the arguments passed to Swift. Edit CalendarManager.swift so it looks like this:
NSLog("%@ %@ %S" nameAll that’s really changed is the NSLog() call to dump the passed variables. Let’s also add a call to the addEvent() method to index.ios.js,just after the console.dir():addEvent(‘One’,125);">‘Two 3);When we run this,and the application crashes. There is an error reported in both Chrome Dev Tools and Xcode and in the simulator.
Note that the NSLog() did work,but the React Native framework displayed the red screen.
The fix for this is to add “nonnull” to the CalendarManagerBridge.m file:
RCT_EXTERN_METHOD:(NSString*)name locationlocation date:(nonnull NSNumberdate)
With this change,the app works without any errors. We can also see in the Xcode console the NSLog() output:
We have verified we can access the arguments passed to our Swift method from JavaScript.
We cannot simply return values to JavaScript because React Native’s JavaScript/Native bridge is asynchronous. That is,you have to implement your Swift method with a callback parameter and call it with a callback function from JavaScript,or you may implement events.
Let’s examine the callback mechanism first. Change the RCT_EXTERN_METHOD line in CalendarManagerBridge.m to read:
date callbackRCTResponseSenderBlockcallbackThis adds a 4th parameter to the method,a callback function. In CalendarManager.swift,we need to alter the the addEvent() method:callbackNSObject() "%@ %@ %@" callback[[ "name""location""date" date ]]) What this version of addEvent() does is call the callback() method with a JavaScript Object that has the argument names/values as key/value pairs. In Swift,we create an NSObject with the [ key: value ] syntax. The argument to the callback from Swift is an array of argument values. In this case,we have just the one Object.We need to modify the JavaScript code in index.ios.js to pass a callback. It should look like this:
"One""Two"functiono consolelogInCallback});When we run this version of the code in the simulator,this is displayed in the JavaScript debugger console:
#import "RCTBridge.h"#import "RCTBridgeModule.h"#import "RCTEventDispatcher.h"
An RCTBridge instance contains an eventDispatcher that we can use to send events to JavaScript from Swift. In order to get this instance,we can have one synthesized for us in our CalendarManager class. We can also verify that it is synthesized by using NSLog() to dump its value.
Modify the class’ code in CalendarManager.swift so it looks like this:
bridgeRCTBridge!// this is synthesized "Bridge: %@"selfbridge callback date There is the bridge member that will be synthesized and in the addEvent() method there is a call to NSLog() to print the value of the bridge. The value printed should be some hex number that’s the address of the bridge instance.When we run the code,we can see that the bridge member is synthesized:
Ultimately debug logging should be wrapped by some other means so the printing can be disabled or directed as you want.
Modify the CalenderManager class one more time so it looks like this:
let ret []([ret])eventDispatchersendAppEventWithName"EventReminder" body retThe sendAppEventWithName() method takes an event name and an arbitrary object that is sent to the JavaScript event handler. In the code above,we’re assigning the NSObject with arguments as key/value pairs to a variable and using it to pass to both the callback() and the event argument.Modify the JavaScript code near the top of index.ios.js so it reads:
'react-native'subscription NativeAppEventEmitteraddListener 'EventReminder' reminder=> consoleEVENT’)'name: '+ reminder'location: 'location'date: ' 'In Callback'})
We’re really just adding the subscription logic before calling addEvent(). When we run this version of the code,we see the expected output in the Chrome console:
From: http://moduscreate.com/swift-modules-for-react-native/