Creating a native module for React Native to read the proximity sensor
React Native Development
Integration with Native Development
- Native Views: In addition to native modules, it is possible to embed native UI components into your React Native app. This is useful when you need a highly customized user experience or want to directly integrate with third-party native libraries.
Turbo Module and Fabric:
In 2018, React Native introduced two new architectures to improve performance and efficiency:
- Fabric: Fabric is a new rendering architecture that aims to improve performance and responsiveness of user interfaces in React Native apps. It was designed to be lighter and more scalable, making apps faster and more resource efficient.
Learn more at: https://reactnative.dev/architecture/overview
Did you know that among the various sensors your phone has, one of them is the proximity sensor? It's through it that your phone turns off the screen when answering a call and bringing it close to your face so as not to overheat next to your skin.
What other applications can we have for this sensor?
- N26 uses it in its app to hide and show values.
- You can create a flap bird style app
- You can add a feature and flip through the pages of an ebook.
Therefore, let's create an RN library for IOS and Android to expose the native proximity sensor APIs. But first, let's understand the differences between the IOS and Android systems for this scenario:
On IOS the proximity sensor API return is a boolean:
true = Near
false = Far
and it behaves according to the graph below, with a buffer range.
The native API documentation is here
The IOS simulators are included in Xcode, Apple's official IDE. They simulate the iOS operating system and device hardware like iPhones and iPads fully in software.
The simulators are fast and easy to use, but do not accurately reproduce real hardware. Some APIs that rely on specific hardware may not work properly. Simulators are best for testing app logic and interface.
For testing our module on iOS, we will have to use a physical device since the proximity sensor is not available on the iOS simulator.
On Android the proximity sensor API return is a numeric value from 0 to 10 cm, but on some devices it can also return a boolean value.
The native Android API documentation is here.
The Android emulators are included in Android Studio, the official IDE. They run a virtual machine with a full version of the Android system. They emulate both software and hardware, including processor, GPU, camera, and sensors.
For testing our module on Android, we will be able to use the emulator since the proximity sensor is available.
Creating a Native Module
To create the native module in React Native it is recommended to use the react-native-builder-bob library.
It is a CLI that provides a module scaffold in React Native.
Let's start with the following command:
❯ npx create-react-native-library@latest react-native-proximity-sensor
We will have to answer some questions and select the module option we want to build. I chose a native module (Native Module) because I want at a later opportunity to migrate it to the newer Turbo Module version.
As well as the languages we want to use in our module.
After these steps, our base project will be generated. In it we have the folders:
ios and android: where the native module codes will be.
example: where a sample application for testing the module is located.
lib: where the transpiled files are after the build.
And the rest of the files are configuration.
This project uses turbo.build to build the packages and yarn workspaces to manage the project's applications. Therefore, at the root of the project you can run the command:
And all dependencies will be installed, including the example project pods.
And to run the example project just run:
❯ yarn example ios
❯ yarn example android
Let's get to code
I won't go into detail on each part of the code, just the main ones so I don't drag on too much. Feel free to access the repository with the complete code and send me questions and suggestions.
In the ProximitySensor.m file we have a method to initialize sensor updates. As this initialization has to be done in the main thread, we need to make an async call to the main queue. You may notice that we have a RCT_EXPORT_METHOD decorator that will indicate this function will be exposed to the JS thread.
With this we will have a callback function proximityStateDidChange, which will be called whenever the sensor value changes:
In the ProximitySensorModule.java file, we add the following import to access the sensor APIs.
And we activate the proximity sensor:
And so, in the callback function when the sensor changes, we build our logic to send an event to JS. In JS we will receive an object with the distance key and another timestamp key with a configurable interval:
Now that we have events with sensor data being sent by native code, we can create a listener to monitor sensor changes in JS:
And in our example app we can consume this listener and when there is a proximity change from far to near and back to far again (is_double_toggle), we can change the visibility state of the balance:
Working on iOS
Here we see our module working on the iPhone (physical device):
Working on Android (Emulator)
Here we see our module working on Android (emulator):
The NPM package can be found here.
The package repository is here. Who is excited to convert this module to React Native's new architecture? Feel free to send your contribution!
See you next time, folks!