Published on

QR Code Scanner in React Native

Authors
  • avatar
    Name
    PatharaNor
    Twitter

Dependency versioning

I testing on :

  • macOS M1
  • React version 18.2.0
  • Android
    • Build tool version 33.0.0
    • NDK version 23.1.7779620
  • iOS
    • Ruby version 2.7.6
    • Cocoapods version 1.11.3
    • Gem version 3.1.6

Prepare environment

Common

Create React Native project :

npx react-native init AutoQRKPaySandbox

Install react-native dependencies to support camera usage and QR code scan :

cd AutoQRKPaySandbox && \
yarn add react-native-camera \
react-native-qrcode-scanner \
react-native-permissions && \
yarn

Create directory src then move App.tsx to src/App.tsx. Don't forget revise import source in index.js :

import App from './src/App'

// ...

iOS

Modify ios/Podfile :

# ...

target 'AutoQRKPaySandbox' do

  permissions_path = '../node_modules/react-native-permissions/ios'
  pod 'Permission-Camera', :path => "#{permissions_path}/Camera"
  pod 'react-native-camera', path: '../node_modules/react-native-camera', subspecs: [
    'TextDetector',
    'FaceDetectorMLKit',
    'BarcodeDetectorMLKit'
  ]

  # ...

end

Then adding key to ios/AutoQRKPaySandbox/Info.plist :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- ... -->

    <key>NSCameraUsageDescription</key>
    <string>Scan QR from K-Payment sandbox</string>
    <!-- Include this only if you are planning to use the camera roll -->
    <key>NSPhotoLibraryUsageDescription</key>
    <string>Access QR image in photo</string>
</dict>
</plist>

Then install Pods from root of the project :

cd ios && (pod install || :) && cd ..

Android

Adding uses-permission for accessing camera, in android/app/src/main/AndroidManifest.xml :

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- ... -->

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <!-- ... -->
</manifest>

With Android 7 and higher you need to add the "Vibration" permission to your AndroidManifest.xml of your project. This should be found in your android/app/src/main/AndroidManifest.xml Add the following:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- ... -->

    <uses-permission android:name="android.permission.VIBRATE"/>

    <!-- ... -->
</manifest>

Adding missingDimensionStrategy for react-native-camera to Android's defaultConfig in android/app/build.gradle :


// ...

android {
    ndkVersion rootProject.ext.ndkVersion

    compileSdkVersion rootProject.ext.compileSdkVersion

    namespace "com.autoqrkpaysandbox"
    defaultConfig {
        // ...

        missingDimensionStrategy 'react-native-camera', 'mlkit'

        // ...
    }

    // ...
}

// ...

Adding QR scanner function to src/App.tsx

import '../ignoreWarnings'

import React, { createRef } from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'

import QRCodeScanner from 'react-native-qrcode-scanner'
import { RNCamera } from 'react-native-camera'

function App(): JSX.Element {
  let scanner = createRef<QRCodeScanner>()

  const onSuccess = (e) => {
    let data = e.data
    console.log('scanned data : ', data)

    try {
      // Set state for success case
    } catch (err) {
      // Set state for fail case
    }
  }

  const doSomthing = () => {
    // Set state

    // To re-activate the scanner to re-scan
    // you need to call "reactivate"
    if (scanner) {
      scanner.current?.reactivate()
    }
  }

  return (
    <QRCodeScanner
      ref={scanner}
      onRead={onSuccess}
      flashMode={RNCamera.Constants.FlashMode.torch}
      topContent={
        <Text style={styles.centerText}>
          Go to <Text style={styles.textBold}>wikipedia.org/wiki/QR_code</Text> on your computer and
          scan the QR code.
        </Text>
      }
      bottomContent={
        <TouchableOpacity onPress={doSomthing} style={styles.buttonTouchable}>
          <Text style={styles.buttonText}>Do somthing</Text>
        </TouchableOpacity>
      }
    />
  )
}

const styles = StyleSheet.create({
  centerText: {
    flex: 1,
    fontSize: 18,
    padding: 32,
    color: '#777',
  },
  textBold: {
    fontWeight: '500',
    color: '#000',
  },
  buttonText: {
    fontSize: 21,
    color: 'rgb(0,122,255)',
  },
  buttonTouchable: {
    padding: 16,
  },
})

export default App

After the first time you scanned QR code, QRCodeScanner will be stuck (not scan). You need to call reactivate() via ref variable to allow it scan QR code again.

Ref. above script at line number 29-31

if (scanner) {
  scanner.current?.reactivate()
}
avatar
PatharaNor
Tech Writer

Usage

Clear old-dependencies :

rm -rf node_modules package-lock.json Gemfile.lock yarn.lock ios/Podfile.lock android/app/build

Install dependencies :

yarn

Pods install :

cd ios && (pod install || :) && cd ..

Let's start :

yarn android

# or
# yarn ios

Issues

ViewPropTypes will be removed from React Native. Migrate to ViewPropTypes exported from 'deprecated-react-native-prop-types

Temporary solution

Create ignoreWarnings.js at root of the project :

import { LogBox } from "react-native";

if (__DEV__) {
  const ignoreWarns = [
    "EventEmitter.removeListener",
    "[fuego-swr-keys-from-collection-path]",
    "Setting a timer for a long period of time",
    "ViewPropTypes will be removed from React Native",
    "AsyncStorage has been extracted from react-native",
    "exported from 'deprecated-react-native-prop-types'.",
    "Non-serializable values were found in the navigation state.",
    "VirtualizedLists should never be nested inside plain ScrollViews",
  ];

  const warn = console.warn;
  console.warn = (...arg) => {
    for (const warning of ignoreWarns) {
      if (arg[0].startsWith(warning)) {
        return;
      }
    }
    warn(...arg);
  };

  LogBox.ignoreLogs(ignoreWarns);

In src/App.tsx, just import the script :

// import at the very top of everything.
import '../ignoreWarnings'

// ...
avatar
PatharaNor
Tech Writer

References