Showing posts with label React. Show all posts
Showing posts with label React. Show all posts

Thursday, July 27, 2017

React Native: Using native code



In this tutorial, we will assume some basic knowledge of Swift and Java, but it is not necessary to follow along.


In my last post, we started with React Native, creating our first project and understanding the basic structure. However, we didn’t get into one of the best features of React Native: being able to easily write native code to get access to native features or simply upgrading the performance.


So we will try writing a small app which will take advantage of native code to add a new contact to the phone on either iOS or Android.

Let’s get started!

Updating from previous code (Ejecting)



First, let’s get a bad part out of the way. We can’t use native code directly if we created our app with “create-react-native-app”, at least not immediately. And that is exactly what we did in the last tutorial. However, this is easy to fix, so don’t worry! We just need to eject our previously created project and we will be good to go.


To eject your project, you will need to have react-native-cli installed, so just run the following command:
yarn global add react-native-cli
#Alternatively you could install this with npm instead if you wish with npm i -g react-native-cli

Once react-native-cli is installed, navigate to your project and run the following command to eject:
npm run eject
You will be prompted to answer some questions to guide the eject process. For this tutorial, we will choose a regular React Native project, and we will use “ContactsProject” as the name for the project.


Lastly just delete the node_modules folder and run “yarn install” and we will be all ready to continue.


One final word about ejecting your project. After doing this you won’t be able to use the QR code as explained in the other post for running the application. Instead, you will need to launch it from the console or from XCode / Android Studio. But this shouldn’t be too much of a deal while continuing your own project.

Connecting with native code



Now that we have our project ready, we can start focusing on the native part. For this tutorial, we will start with a fresh projected as created by create-react-native-app, and ejected as described above.

Connecting with iOS (Swift)



First, we will start by creating a new swift file (we will call it “ContactsManager.swift”) in your project folder that it’s inside the “ios” folder. It is recommended this is done with XCode since you will be immediately prompted to create a bridging header file (“ProjectName-Bridging-Header.h”), which we will need. If you don’t do it this way it can be added later, so don’t worry.

Inside “ContactsManager.swift” let’s add the following code:


import Foundation
import Contacts
@objc(ContactsManager)
class ContactsManager: NSObject {
 
}
We just created a new class called “ContactsManager” which will be where the logic for adding the contact will be done later. One thing to note is the “objc” modifier. React Native will communicate with Objective-C, so this is an extra step to pass Swift code to Objective-C. If you feel more comfortable with Objective-C you can code directly in Objective-C and skip this, but in this tutorial, we will continue with Swift.


Let’s add now add our function to add the new contact. I won’t go into how exactly this function works since we should concentrate more on linking the code than the adding of the contact, but it should be self-explanatory.


Add the following code inside that class:


@objc(addContact:familyName:number:)
 func addContact(name: NSString, familyName: NSString, number: NSString) -> Void {
   
   if #available(iOS 9.0, *) {
     let contact = CNMutableContact()
     
     contact.givenName = name as String
     contact.familyName = familyName as String
     
     contact.phoneNumbers = [CNLabeledValue(
       label:CNLabelPhoneNumberiPhone,
       value:CNPhoneNumber(stringValue:number as String))]
     
     // Saving the newly created contact
     let store = CNContactStore()
     let saveRequest = CNSaveRequest()
     saveRequest.add(contact, toContainerWithIdentifier:nil)
     try! store.execute(saveRequest)
     
   }
 }
We need to use “objc” in the function too. As you can see, the function itself is the same as how we would do it natively. Nothing too complex here.


Now we have all the native code that we would like to run in our React Native app, but how to do it? Well, first we need to indicate the classes and functions we would like to be accessed externally in the JavaScript. For this we will need a bridge file (“ContactsManagerBridge.m”) as follows:


#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(ContactsManager, NSObject)
RCT_EXTERN_METHOD(addContact:(NSString *)name familyName:(NSString *)familyName number:(NSString *)number)

@end
In this, we indicate the class, the methods and the parameters that will be used by our JavaScript code.


The last piece of native code we need is in the bridging header which we created at the start of this section. In that file you just need to indicate that you will be using the Bridge module with this line:


#import <React/RCTBridgeModule.h>
And we are finished with the iOS part. Depending on the version of iOS you are using you may need to modify the .plist to include permission text for using Contact, but that should be easy to fix.


So let’s return to our “App.js” where the application will start. Here, we can use the following code:



import React from 'react';
import { StyleSheet, Button, View } from 'react-native';
import { NativeModules } from 'react-native';
export default class App extends React.Component {
render() {
  return (
    <View style={styles.container}>
        <Button onPress={addContact} title="Add contact"/>
    </View>
  );
}
}
function addContact(){
  let ContactsManager = NativeModules.ContactsManager;
  ContactsManager.addContact('John', 'Doe', '(555) 424-5920');
}
const styles = StyleSheet.create({
container: {
  flex: 1,
  backgroundColor: '#fff',
  alignItems: 'center',
  justifyContent: 'center',
},
});

As you can see, this will just display a simple screen with just one button, and when that button is pressed it will call the “addContact” function. This function is what calls the native code we have been implementing until now. It brings “ContactsManager” from the native modules and calls the function we created before.


And now you are ready! Just run this project, which you can easily do from XCode, and you will see the App. After clicking the button in the middle of the App, a new contact called “John Doe” should be added to the Contacts on your phone.


Connecting with Android (Java)



First, we will start by creating a new Java file in your android project folder (we will call it “ContactsManager.java”). This is where we will write our Java code to be executed by our application.


For our example, we will create a simple class called “ContactsManager”, which will extend ReactContextBaseJavaModule. This allows us to later export this class as a native module to our React Native JavaScript. This class should also override the “getName” method, returning the name by which we will access this module on the JavaScript, as it is shown in the following code:



public class ContactsManager extends ReactContextBaseJavaModule { public ContactsManager(ReactApplicationContext reactContext) { super(reactContext); } @Override public String getName() { return "ContactsManager"; } }


Now we can add our method that we wish to call from the JavaScript. This is done exactly as we would for a native android app. The only difference is that we need to add “@ReactMethod” before the declaration.


Just add the following code to the Java file:


@ReactMethod public void addContact(String firstName, String lastName, String phoneNumber) { Context contetx = getReactApplicationContext(); String strDisplayName = firstName + " " + lastName; String strNumber = phoneNumber; ArrayList<ContentProviderOperation> cntProOper = new ArrayList<ContentProviderOperation>(); int contactIndex = cntProOper.size(); cntProOper.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null).build()); cntProOper.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID,contactIndex) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, strDisplayName) .build()); cntProOper.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID,contactIndex) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, strNumber) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE).build()); try { ContentProviderResult[] contentProresult = null; contentProresult = contetx.getContentResolver().applyBatch(ContactsContract.AUTHORITY, cntProOper); } catch (Exception exp) { }
}
Since we have finished with preparing the module we need to work on exporting it. For this, we will create a new file (we will call it “ContactsPackage.java”) where we will package modules to be later used in our React Native app. In this, we will create a new class which implements “ReactPackage”. We could add many modules in one package, but for this example, just the one we created before is enough.


Add the following code to the new package file:


public class ContactsPackage implements ReactPackage{ @Override public List<Class<? extends JavaScriptModule>> createJSModules() { return Collections.emptyList(); } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List<NativeModule> createNativeModules( ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new ContactsManager(reactContext)); return modules; } }
Lastly, we need to indicate that this package should be used in our React Native app. For this, we need to open the MainApplication.java file and add our package to the “getPackages” function. The code below shows how this would be done:


@Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new ContactsPackage() ); }
With this, we have finished doing everything on the Java side to export and use our custom native module. For this particular example, you should also add the following permission to your AndroidManifest, but this is obviously not needed for other purposes:


<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
So let’s return to our “App.js” where the application will start. Here, we can use the following code:

import React from 'react';
import { StyleSheet, Button, View } from 'react-native';
import { NativeModules } from 'react-native';
export default class App extends React.Component {
render() {
  return (
    <View style={styles.container}>
        <Button onPress={addContact} title="Add contact"/>
    </View>
  );
}
}
function addContact(){
  let ContactsManager = NativeModules.ContactsManager;
  ContactsManager.addContact('John', 'Doe', '(555) 424-5920');
}
const styles = StyleSheet.create({
container: {
  flex: 1,
  backgroundColor: '#fff',
  alignItems: 'center',
  justifyContent: 'center',
},
});
As you can see, this will just display a simple screen with just one button, and when that button is pressed it will call the “addContact” function. This function is what calls the native code we have been implementing until now. It brings “ContactsManager” from the native modules and calls the function we created before.


And now you are ready! Just run this project, which you can easily do from XCode, and you will see the App. After clicking the button in the middle of the App, a new contact called “John Doe” should be added to the Contacts on your phone.


Conclusion



As you can see, incorporating native code into your React Native application is not too complicated. You can write your native code as you normally would and just do some minor bridging inside the project to indicate which functionalities should be called from where.


You may also note (if you followed both the Android and iOS sections) that we have named the module the same in both cases. React Native plays nice enough to know exactly which module to use depending on where the code is running, so you don’t even need to handle the different use for the different platforms.
If you wish to see the example code in more detail, feel free to check our github project.