React Native Publishing an Android App

If you are a mobile developer and haven’t tried React Native, you probably should. React Native makes it so much easy to build simple, beautiful apps with almost just Javascript. It also allows developers to write native code for each platform (iOS, Android, Windows and more) and use it via Javascript. These are called Native modules. There are plenty of libraries out there that helps you use some of the key device features such as Bluetooth, Wifi, camera that uses native code and expose interfaces to Javascript via NativeModules fromReactNative module.

Here are some of the popular react-native libraries that are also comes with platform specific native modules

Prior to RN 0.60, to use a library that is also a native module, we need to add the dependency to package.json and also link it to our native projects so that it is included in our native builds. This can be done either using react-native link <dependency name> or manually if you have super powers.

In case of Android, the linking is as simple as adding the dependency project’s path to settings.gradle and adding an implementation <dependency package> in build.gradle and also include the package in your MainApplication.java file.

For example, if you were to add the library react-native-ble-manager to your project, you need to follow these steps:

Disclaimer : I totally stole these from their README

// file: android/app/build.gradle
...

dependencies {
    ...
    compile project(':react-native-ble-manager')
}
// file: android/settings.gradle
...

include ':react-native-ble-manager'
project(':react-native-ble-manager').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-ble-manager/android')

Add the lines to your MainApplication.java:

import it.innove.BleManagerPackage; // <--- import

public class MainApplication extends Application implements ReactApplication {

    ...

    @Override
    protected List getPackages() {
        return Arrays.asList(
            new MainReactPackage(),
            new BleManagerPackage() // <------ add the package
        );
    }

    ...
}

Note: This post concentrates on Autolinking on Android, take a look at here for how it used to be for iOS : 👉 Linking iOS Libraries

Basically for Android, there are 2 steps involved in linking a native library to react-native projects . For each dependency,

  1. Include the dependency project in settings.gradle
  2. Include the implementation <dependency project> in our dependency {…} in build.gradle file.
  3. Include the package in our Application class.

As of React native 0.60+, the react-native cli was extracted out and maintained separately into the react-native-community/cli repo which had a major version update. This update eliminates the need for us to manually link the native libraries after adding them to package.json.

Note: RN 0.60+ requires cocoapods for iOS and AndroidX for Android. I will write more about it on a separate post.

For example, if you were to add the same library react-native-ble-manager to your project with RN 0.60+, all you have to do is just add react-native-ble-manager to your package.json. This makes it so much easy to maintain and easily update our dependencies.

How does this Auto-linking work for Android projects?

We discussed how to link native libraries manually prior to RN 0.60. Setting up Autolinking with RN 0.60+ is very similar to the 3-step manual linking for each dependency. Below steps are just a one time setup.

1. Including Project in settings.gradle

Instead of adding individual native libraries to our settings.gradle file, we just include one script native_modules.gradle that comes with react-native-cli 2.0+ and then invoke the function applyNativeModulesSettingsGradle(settings).

apply from: "../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"
applyNativeModulesSettingsGradle(settings)

2. Include the implementation for each dependency we added

This is similar to the previous step, we include the gradle script native_modules.gradle again and invoke the function applyNativeModulesAppBuildGradle(project) which will take care of adding implementation <dependency project> automatically.

3. Include the package in our Application class.

The native_modules.gradle generates a class called PackageList which has all the dependencies’ packages included. We just need to instantiate this class using the ApplicationContext and return it to the getPackages method of our Application class. If there are additional native modules need to be included, we can just add them to this PackageList instance.

What is in the file native_modules.gradle that makes this process so simple?

We need to thank its Javascript counter part for that. The react-native-cli has this pretty neat CLI tool config. This tool provides us all the necessary information about the react native project including the native dependencies that should be added to our native projects (iOS, Android, etc). This is how it looks for a project that has a dependency react-native-ble-manager :

$ npx react-native config
{
  "root": "/Users/bala/sample-app/Sample",
  "reactNativePath": "/Users/bala/sample-app/Sample/node_modules/react-native",
  "dependencies": {
    "react-native-ble-manager": {
      "root": "/Users/bala/sample-app/Sample/node_modules/react-native-ble-manager",
      "name": "react-native-ble-manager",
      "platforms": {
        "android": {
          "sourceDir": "/Users/bala/sample-app/Sample/node_modules/react-native-ble-manager/android",
          "folder": "/Users/bala/sample-app/Sample/node_modules/react-native-ble-manager",
          "packageImportPath": "import it.innove.BleManagerPackage;",
          "packageInstance": "new BleManagerPackage()"
        }
       "iOS":{…}
      },
      "assets": [],
      "hooks": {},
      "params": []
    }
  },
  "commands": [],
  "assets": [],
  "project": {
    "ios": {
      ...
    },
    "android": {
      "sourceDir": "/Users/bala/sample-app/Sample/android/app",
      "isFlat": false,
      "folder": "/Users/bala/sample-app/Sample",
      "stringsPath": "/Users/bala/sample-app/Sample/android/app/src/main/res/values/strings.xml",
      "manifestPath": "/Users/bala/sample-app/Sample/android/app/src/main/AndroidManifest.xml",
      "buildGradlePath": "/Users/bala/sample-app/Sample/android/app/build.gradle",
      "settingsGradlePath": "/Users/bala/sample-app/Sample/android/settings.gradle",
      "assetsPath": "/Users/bala/sample-app/Sample/android/app/src/main/assets",
      "mainFilePath": "/Users/bala/sample-app/Sample/android/app/src/main/java/com/sample/MainApplication.java",
      "packageName": "com.sample"
    }
  }
}

I removed most of the stuff in this output to keep it short. Take a closer look at the android property of dependencies.react-native-ble-manager.

"android": {
          "sourceDir": "/Users/bala/sample-app/Sample/node_modules/react-native-ble-manager/android",
          "folder": "/Users/bala/sample-app/Sample/node_modules/react-native-ble-manager",
          "packageImportPath": "import it.innove.BleManagerPackage;",
          "packageInstance": "new BleManagerPackage()"
        } 

The script uses this information to auto-link native libraries to our project automatically — for each dependency 🤯

Learn more about Auto-linking 👉 here

Pro Tip: When you migrate to RN 0.60+, you may need to unlink all your previously “linked” native libraries. You can do it by using npx react-native unlink <dependency name> . This removes the entry from your settings.gradle , build.gradle and MainApplication.java . If you have already moved your MainApplication class to Kotlin, then this unlinking will not work. You need to unlink manually, which is the reverse of manual linking.