This React Native module allows a React Native application to use and control Socket Mobile wireless barcode scanners, NFC Reader/Writer, and Camera to capture and deliver data capture to such application.
The React Native CaptureSDK fully supports the React Native new architecture.
| Devices | < 2.0 | 2.0 |
|---|---|---|
| SocketCam C860 | ✅ | ✅ |
| SocketCam C820 | ✅ | ✅ |
| S720/D720/S820 | ✅ | ✅ |
| D600, S550, and all other barcode scanners | ✅ | ✅ |
| S370 | ✅ | ✅ |
| S320 | ✅ | ✅ |
| S721 (new Bluetooth LE barcode scanner) | ❌ | ✅ |
Version 2.0 introduces CaptureHelper, a high-level lifecycle manager that replaces the manual CaptureRn + onCaptureEvent switch/case pattern. Instead of constructing CaptureProperty objects and interpreting raw events yourself, you create a CaptureHelper instance with typed callbacks and call named methods.
import {
CaptureHelper,
type CaptureHelperDevice,
type AppInfoRn,
type DecodedData,
SocketCamTypes,
BluetoothDiscoveryMode,
Trigger,
} from 'react-native-capture';
const helper = new CaptureHelper({
appInfo,
onDeviceArrival: (device) => console.log(`Connected: ${device.name}`),
onDeviceRemoval: (device) => console.log(`Disconnected: ${device.name}`),
onDecodedData: (data, device) => console.log(`Scanned: ${data.data as number[]}`),
onError: ({ code, message }) => console.error(`Error ${code}: ${message}`),
});
await helper.open();Connected devices are now wrapped in CaptureHelperDevice objects that expose typed methods. No more building CaptureProperty by hand:
// Before (v1.x) — manual property construction
const property = new CaptureProperty(
CapturePropertyIds.FriendlyNameDevice,
CapturePropertyTypes.None,
{},
);
const result = await device.devCapture.getProperty(property);
// After (v2.0) — typed method
const name = await device.getFriendlyName();Available device methods include getFriendlyName, setFriendlyName, getBatteryLevel, getFirmwareVersion, setTrigger, getDataSource, setDataSource, setDataConfirmation, getStandConfig, setStandConfig, and more.
Version 2.0 adds support for Bluetooth LE devices such as the S721. These devices use a manual discovery flow that the SDK handles for you. See Connecting Bluetooth LE Devices.
CaptureHelper was previously a static utility class with methods like CaptureHelper.setSocketCamEnabled(...). It is now an instance-based lifecycle manager that must be created with new CaptureHelper({...}) and opened with helper.open().
Callbacks like onDeviceArrival now receive CaptureHelperDevice instances instead of raw CaptureDeviceInfo. These objects have typed methods (getFriendlyName(), getBatteryLevel(), etc.) and properties (name, guid, type, handle).
The underlying CaptureRn instance is still accessible via device.devCapture if needed.
getBatteryLevel() now returns a plain number (0–100) instead of a bit-shifted value that required manual decoding.
The following interfaces have been removed: GetSocketCamEnabledArguments, SetSocketCamEnabledArguments, SocketCamTriggerButtonData, SocketCamStatusOption, SocketCamStatusSubOption.
Users who want to use Bluetooth LE readers have two options:
Option A — Install the Socket Mobile Companion app which handles discovery and pairing.
Option B — Implement the discovery flow in your own UI using the SDK's discovery methods. See Connecting Bluetooth LE Devices.
yarn add react-native-captureimport React, { useState, useEffect, useRef } from 'react';
import {
CaptureHelper,
type CaptureHelperDevice,
type AppInfoRn,
type DecodedData,
type DiscoveredDeviceInfo,
SocketCamTypes,
BluetoothDiscoveryMode,
Trigger,
} from 'react-native-capture';
const appInfo: AppInfoRn = {
appIdIos: 'ios:com.mycompany.myapp',
appKeyIos: 'MC0C...',
appIdAndroid: 'android:com.mycompany.myapp',
appKeyAndroid: 'MC0C...',
developerId: 'your-developer-id',
};
const App = () => {
const [devices, setDevices] = useState<CaptureHelperDevice[]>([]);
const [decodedData, setDecodedData] = useState<DecodedData | null>(null);
const [status, setStatus] = useState('Opening Capture...');
const helperRef = useRef<CaptureHelper | null>(null);
useEffect(() => {
const helper = new CaptureHelper({
appInfo,
onDeviceArrival: (device) => {
setDevices(d => [...d, device]);
},
onDeviceRemoval: (device) => {
setDevices(d => d.filter(dd => dd.guid !== device.guid));
},
onDecodedData: (data, device) => {
setDecodedData(data);
},
onError: ({ code, message }) => {
setStatus(`Error ${code}: ${message}`);
},
});
helperRef.current = helper;
helper
.open()
.then(() => setStatus('Capture open'))
.catch((err) => setStatus(`Failed: ${err?.error?.message}`));
return () => {
helper.close().catch(() => {});
};
}, []);
// ...
};Once a device arrives, you can call methods directly on the CaptureHelperDevice:
const device = devices[0];
// Get device info
const friendlyName = await device.getFriendlyName();
const firmware = await device.getFirmwareVersion();
// firmware: { major, middle, minor, build, year, month, day }
// Battery (returns 0–100 directly)
const battery = await device.getBatteryLevel();
// Trigger a scan
await device.setTrigger(Trigger.Start);
// Enable/disable a symbology
await device.setDataSource(CaptureDataSourceID.SymbologyQRCode, CaptureDataSourceStatus.Enable);interface CaptureHelperCallbacks {
onDeviceArrival?: (device: CaptureHelperDevice) => void;
onDeviceRemoval?: (device: CaptureHelperDevice) => void;
onDecodedData?: (data: DecodedData, device: CaptureHelperDevice) => void;
onSocketCamCanceled?: (device: CaptureHelperDevice) => void;
onDiscoveredDevice?: (device: DiscoveredDeviceInfo) => void;
onDiscoveryEnd?: (result: number) => void;
onBatteryLevel?: (level: number, device: CaptureHelperDevice) => void;
onPowerState?: (state: PowerState, device: CaptureHelperDevice) => void;
onButtons?: (state: Notifications, device: CaptureHelperDevice) => void;
onError?: (error: { code: number; message: string }) => void;
}// Lifecycle
await helper.open();
await helper.close();
// Get connected devices
const devices = helper.getDevices();
// Access the underlying CaptureRn instance (needed for SocketCamViewContainer)
const rootCapture = helper.rootCapture;
// SDK info
const version = await helper.getVersion();
// SocketCam
await helper.setSocketCamEnabled(true);
await helper.getSocketCamEnabled();
// BLE discovery (see next section)
await helper.addBluetoothDevice(BluetoothDiscoveryMode.BluetoothLowEnergy);
await helper.connectDiscoveredDevice(discoveredDevice);
await helper.removeBleDevice(connectedDevice);For Bluetooth LE products (S721, D600, S550, S370...) the SDK uses a manual discovery flow. With CaptureHelper, this is straightforward:
Call addBluetoothDevice on your CaptureHelper instance. Each nearby device triggers the onDiscoveredDevice callback.
const [discoveredDevices, setDiscoveredDevices] = useState<DiscoveredDeviceInfo[]>([]);
// In your CaptureHelper callbacks:
const helper = new CaptureHelper({
appInfo,
onDiscoveredDevice: (device) => {
setDiscoveredDevices(d => {
if (d.find(dd => dd.identifierUuid === device.identifierUuid)) return d;
return [...d, device];
});
},
onDiscoveryEnd: () => {
setStatus('Discovery finished');
},
// ...other callbacks
});
// Start scanning
await helper.addBluetoothDevice(BluetoothDiscoveryMode.BluetoothLowEnergy);const connectDevice = async (device: DiscoveredDeviceInfo) => {
await helper.connectDiscoveredDevice(device);
// A successful connection triggers onDeviceArrival with a CaptureHelperDevice
};const disconnectDevice = async (device: CaptureHelperDevice) => {
await helper.removeBleDevice(device);
// Triggers onDeviceRemoval
};SocketCam (C820 / C860) lets your app use the device camera as a barcode scanner. In version 2.0, the SocketCamViewContainer component handles the native camera view and the SocketCam extension lifecycle.
// Toggle SocketCam on — this triggers a DeviceArrival for the SocketCam virtual device
await helper.setSocketCamEnabled(true);
// The SocketCam device appears in your devices list
const socketCamDevice = devices.find(d => SocketCamTypes.indexOf(d.type) > -1);Import and use the SocketCamViewContainer component where you want the camera view:
import { SocketCamViewContainer, Trigger, SocketCamTypes } from 'react-native-capture';
<SocketCamViewContainer
openSocketCamView={openSocketCamView}
handleSetSocketCamEnabled={handleSetSocketCamEnabled}
clientOrDeviceHandle={helper.rootCapture?.clientOrDeviceHandle}
triggerType={Trigger.Start}
socketCamCapture={helper.rootCapture}
socketCamDevice={socketCamDevice}
handleSetStatus={setStatus}
handleSetSocketCamExtensionStatus={setExtensionStatus}
socketCamCustomModalStyle={{
presentationStyle: 'overFullScreen',
animationType: 'fade',
transparent: true,
}}
socketCamCustomStyle={customStyles}
androidSocketCamCustomView={<MyCustomAndroidView />}
/>Android note: On Android, mount SocketCamViewContainer as soon as helper.rootCapture is available (before the user enables SocketCam). This starts the SocketCam extension process in the background. Without this, enabling SocketCam returns error -32 (ESKT_NOTAVAILABLE).
For more on SocketCam, check out the docs. For custom views, read more here.
// ❌ Before (v1.x)
const capture = new CaptureRn();
await capture.open(appInfo, onCaptureEvent);
// ...then a big switch/case in onCaptureEvent
// ✅ After (v2.0)
const helper = new CaptureHelper({
appInfo,
onDeviceArrival: (device) => { /* ... */ },
onDecodedData: (data, device) => { /* ... */ },
// ...
});
await helper.open();// ❌ Before
const prop = new CaptureProperty(CapturePropertyIds.FriendlyNameDevice, CapturePropertyTypes.None, {});
const result = await deviceCapture.getProperty(prop);
const name = result.value;
// ✅ After
const name = await device.getFriendlyName();// ❌ Before
await CaptureHelper.setSocketCamEnabled({ socketCamCapture, ... });
// ✅ After
await helper.setSocketCamEnabled(true);// ❌ Before — manual DeviceManagerArrival handling, CaptureProperty for AddDevice
// ✅ After
await helper.addBluetoothDevice(BluetoothDiscoveryMode.BluetoothLowEnergy);
// onDiscoveredDevice callback fires for each device found
await helper.connectDiscoveredDevice(discoveredDevice);The low-level CaptureRn class and all property types/enums are still exported and can be used directly if you need full control. CaptureHelper is the recommended approach for most applications.
In your build.gradle file, add the Socket Mobile release repo in the repositories section:
maven {
url "https://bin.socketmobile.com/repo/releases"
}In your app/build.gradle, add:
packagingOptions {
pickFirst '/lib/arm64-v8a/libc++_shared.so'
pickFirst '/lib/armeabi-v7a/libc++_shared.so'
}To enable Start Capture Service on Android (so that the developer doesn't need to actively run/open Companion), add a network security configuration.
In your-app/android/app/src/main/res/xml/network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false" />
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">localhost</domain>
<domain includeSubdomains="false">127.0.0.1</domain>
</domain-config>
</network-security-config>In AndroidManifest.xml, add to the <application> tag:
android:networkSecurityConfig="@xml/network_security_config"And before the closing </manifest> tag:
<queries>
<package android:name="com.socketmobile.companion"/>
</queries>For more details, see the Android docs.
In your Info.plist, add camera access and the Companion URL scheme:
<key>NSCameraUsageDescription</key>
<string>Need to enable camera access for SocketCam products</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>sktcompanion</string>
</array>More documentation available at Socket online documentation.