Ever since it was announced 2013, React has been gaining more and more traction in the world of software development. Reactive Design Pattern has become a well-known term in itself, no longer tied to the world of Facebook's React (although the origin of the term is somewhat contested).
Shortly thereafter, in 2015, the development team at Facebook also delved into the world of mobile development, creating their own framework called React Native. I, like most developers, was skeptical at first – yet another platform, just make things more complicated. And for the first few years, I was probably right to be skeptical, as react native has gone through several "phases", introducing radical updates that have brought more pain than pleasure to developers
However, in the past few years, React Native has "grown up" and has proven itself to be a formidable force in the world of Cross-Platform Mobile Development. But I digress, for the time being, I am not here to compare mobile frameworks. Let's build a Scanbot Document Scanner App with React Native!
First off, React Native is not "magic". In layman's terms, it's just a blanket on top of a native iOS and Android app that offers and unified API for rendering views. Therefore, we require the same basic mobile development tools as if we were doing it in Java or Objective-C:
- Xcode & CocoaPods
- Android Studio
Then, it's wise to have java and path set in your path. It's wise, but if you're on a clean install of Android Studio, haven't made any previous java- or gradle-related installs, it shouldn't be necessary.
This will let the React CLI know which java to use (in case you've downloaded several versions of java). I advise to use OpenJDK 8 for Android development (11 should be fine, 14 is iffy with older versions of gradle). You can just download and in stall it – a couple o' clicks and you're done.
Then, on a Mac or Linux, go to /Users/$USER/
and open .bash_profile
, .zshrc
or .bashrc
with you preferred text editor and add the following lines to it:
export JAVA_HOME=/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
export ANDROID_HOME=/Users/$USER/Library/Android/sdk
export PATH=${PATH}:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools
After that, source <filename>
to reload the file. That's it!
Note that I've also added platform-tools
to my path. That is not required, but very handy for debugging all possible things, most notably the command adb devices
will show whether you have a debuggable device available or not.
And finally, the tools required for the React Native layer to properly function:
I personally prefer installing everything through brew
. The setup is quite simple. First, install brew:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
Then, make sure it's up to date and install node
:
brew update
brew install node
And that's it. You're now just about ready to start coding. One last hint from the official React Native environment setup guide:
React Native has a built-in command line interface. Rather than install and manage a specific version of the CLI globally, we recommend you access the current version at runtime using
npx
, which ships with Node.js. Withnpx react-native <command>
, the current stable version of the CLI will be downloaded and executed at the time the command is run.
The hard part was setting up the environment. From now on it's smooth sailing ⛵
We'll be using typescript for this example, as it is the recommended approach for writing javascript these days (besides, I personally prefer strongly-typed languages, much less #yolo). Run the following:
npx react-native init ScanbotApp --template react-native-template-typescript
That should take a couple of minutes but it's also an awesome one-liner that does absolutely everything for you: everying from creating and configuring the native projects to npm install. After that's done, simply:
cd ScanbotApp
react-native run-android
And you should be greeted by the following:
Now we finally get to the good stuff – Scanbot! If you're reading this, you're probably aware of what npm
is, nevertheless, in short, your "Hello World" app has a file called package.json
that defines all the dependencies you have. As you can see, react-native itself already defines a whole bunch of them, everything from linters to typescript to jsx syntax.
Let's add another one there (to dependencies
, not devDependencies
, as we need the ScanbotSDK during runtime as well, not just for development purposes) and then install it via npm install
:
"react-native-scanbot-sdk": "4.1.0"
Since ScanbotSDK for react-native is a wrapper of the native SDK, an additional step is to also download the native SDK. As of react-native version 0.62
, native dependencies are automatically resolved and you do not need to add anything to the Podfile
(iOS native equivalent of package.json
), but you do still have to go to the iOS folder and install native dependencies:
cd ios
pod install
That was almost too easy, wasn't it? Well, this isn't over yet...
As with most things in mobile development, you expect the android equivalent of something to be a bit more difficult to configure. This isn't necessarily a bad thing, quite the opposite, having more options to configure is mostly a good a thing. It can get somewhat frustrating, though.
To configure gradle-level requirements, we advise Android Studio, as it supports better syntax highlighting and a visual interface. You are, of course, free to use any text editor – even Nodepad will do the trick.
First, you should enable multidex. That's done via adding multiDexEnabled true
to your android/app/build.gradle
as such:
defaultConfig {
applicationId "com.scanbotapp"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
multiDexEnabled true
}
Enabling MultiDex is just a fancy way of saying "Yes, I know this app has a heavy-weight ML-based document scanning SDK and I don't care".
Right there in the same file, you should also specify that it should pick the first instance of libc++_shared.so
that it finds, on the same level as defaultConfig
, as follows:
packagingOptions {
pickFirst '**/libc++_shared.so'
}
This needs to be specified because many third-party dependencies have native binaries that use the c++ shared library (in the context of gradle, react-native itself is a third-party dependency that uses it, and so does ScanbotSDK).
Another thing you should add is android:largeHeap="true"
and that under your application tag of android/app/src/main/AndroidManifest.xml
file, as such:
<application
android:largeHeap="true"
android:name=".MainApplication"
...
That's just a fancy way of saying what MultiDex is for, except that multidex is for compile-time, largeHeap is for runtime.
Next, you should also add ScanbotSDK paths to your application-level gradle (android/build.gradle
). That way, gradle knows exactly where to find our SDK:
allprojects {
repositories {
...
// Scanbot SDK Maven repositories:
maven { url 'https://nexus.scanbot.io/nexus/content/repositories/releases/' }
maven { url 'https://nexus.scanbot.io/nexus/content/repositories/snapshots/' }
}
}
Right there in your application-level gradle, you should also increase the minSdkVersion
to 21
, which is a requirement for the latest versions of ScanbotSDK (and, unless specifically requested otherwise, in general a good idea – supporting backwards compatibility below Android 5.0 is a nightmare).
And finally, specify where react-native-scanbot-sdk
is located in your repository. That can be achieved by adding the following lines to android/settings.gradle
:
include ':react-native-scanbot-sdk'
project(':react-native-scanbot-sdk').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-scanbot-sdk/android/app')
Wipes sweat off forehead... And that's all there is to it!
If we're both using the the same app template, you should also be able to do the following. For the sake of simplicity, slap a click-handler to the to a TextView
, e.g. the following line:
<Text style={styles.sectionTitle} >Step One</Text>
becomes:
<Text
style={styles.sectionTitle}
onPress={() => {
console.log('Hello click-handler!');
}}>
Step One
</Text>
Then, let's just create a global variable to decide what our click-handler should actually do:
let initialized = false;
Now that the basics are out of the way, it's time to start working with the SDK.
Before we begin, you should define the imports. For this example, we just need three things: the SDK itself, and then to option and configuration classes:
import ScanbotSDK, {
BarcodeScannerConfiguration,
InitializationOptions,
} from 'react-native-scanbot-sdk/src';
The first real step is initialisation. That goes as follows:
const options: InitializationOptions = {
licenseKey: '',
};
await ScanbotSDK.initializeSDK(options);
Similarily, to start the QR- and Barcode scanner, the snippet is as follows:
const configuration: BarcodeScannerConfiguration = {};
const result = await ScanbotSDK.UI.startBarcodeScanner(configuration);
Just to review, your entire onPress
should look like this:
onPress={async () => {
if (!initialized) {
const options: InitializationOptions = {
licenseKey: '',
};
await ScanbotSDK.initializeSDK(options);
initialized = true;
Alert.alert("SDK initialized", "Press me again to start Barcode Scanner");
} else {
const configuration: BarcodeScannerConfiguration = {};
const result = await ScanbotSDK.UI.startBarcodeScanner(configuration);
if (result?.barcodes?.length > 0) {
Alert.alert("Barcode detected", result.barcodes[0].text);
}
}
}}>
Now that we've got everyting sorted out, go ahead and scan the following QR code:
// TODO advanced sample, displaying scanned documents, updating texts etc
While every cross-platform solution does have its problems in the form of internal inconsistencies, the simplicity and and elegance of react does make software development very smooth and, dare I say, "fun".
I'm sure that, at this point, you'd like to know more about the possibilities with react-native and ScanbotSDK. Here are a few links for you:
- Explore the features of ScanbotSDK in our example app
- If you've had enough of coding for now, that's fine, everyone needs a break. Then, have a look at our extensive documentation
TODO Notes:
- Title
- Introduction a bit much?
- Inconsistent level of technical detai
- Conclusion?