Skip to main content

React Native Bluetooth (BLE)

Note: Demo: React Native / Expo Example → react-native-demo

Use this route when the app is a pure React Native BLE integration. It talks through @ukeyfe/hardware-ble-sdk and @ukeyfe/hardware-transport-react-native directly, so no WebView or custom low-level transport plug-in is required; once the device is connected, chain APIs are called through the same SDK shape as the other transports.

Setup

Add these packages to the app project first, so autolinking can include the BLE native module in your Dev Client or production build:

npm i @ukeyfe/hardware-ble-sdk @ukeyfe/hardware-transport-react-native \
@ukeyfe/react-native-ble-utils react-native-ble-plx \
buffer process react-native-get-random-values react-native-url-polyfill

First load the polyfill in the application entry file (such as index.js or App.tsx), and then register the RN transport side effects:

// Add the runtime shims your RN app actually needs
import "react-native-get-random-values";
import "react-native-url-polyfill/auto";
// @ts-ignore
global.Buffer = global.Buffer || require("buffer").Buffer;
// @ts-ignore
global.process = global.process || require("process");

// Import this before SDK init so the RN transport can register itself
import "@ukeyfe/hardware-transport-react-native";

import ukeySdk from "@ukeyfe/hardware-ble-sdk";

Android target settings

For Android 12 and later, place these permission declarations in AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

Manifest entries alone are not enough: request BLUETOOTH_SCAN and BLUETOOTH_CONNECT at runtime as well. On some OS versions, location permission and the system location switch also affect BLE discovery, and missing either one can leave the scan list empty.

iOS target settings

On iOS, add these usage descriptions to Info.plist:

  • NSBluetoothAlwaysUsageDescription: required Bluetooth usage text.
  • NSBluetoothPeripheralUsageDescription: keep it if you still need compatibility with older iOS releases.
  • NSCameraUsageDescription: only include this when the app actually uses the camera, such as for QR scanning.

For Expo projects, put the same declarations under expo.ios.infoPlist and expo.android.permissions in app.json.

Metro configuration

React Native does not provide every Node core module by default, so Metro needs these aliases plus package exports support for dependency sub-path imports:

// Reference file: metro.config.js
config.resolver.extraNodeModules = {
buffer: require.resolve("buffer/"),
crypto: require.resolve("crypto-browserify"),
stream: require.resolve("stream-browserify"),
process: require.resolve("process/browser"),
events: require.resolve("events/"),
http: require.resolve("http-browserify"),
https: require.resolve("https-browserify"),
zlib: require.resolve("browserify-zlib"),
util: require.resolve("util/"),
url: require.resolve("url/"),
path: require.resolve("path-browserify"),
};
config.resolver.unstable_enablePackageExports = true;

Build Dev Client (real device)

Expo Go will not bundle your app's custom BLE native module. Test with a Dev Client or a real-device build instead:

# iOS target
expo run:ios --device

# Android target
expo run:android --device

# Start the bundler for Dev Client
expo start --dev-client -c

Initialize and subscribe to events

export async function initializeRnBleSession() {
await ukeySdk.init({
env: "react-native", // Use the React Native BLE transport
debug: __DEV__,
fetchConfig: true,
});

// Subscribe to UI events (PIN / Passphrase / confirmation) early
// Reference: ukeySdk.on(UI_EVENT, (uiEvent) => { ... });
}

Discover devices and identify device_id

const availableDevices = await ukeySdk.searchDevices();
if (!availableDevices.success) throw new Error(availableDevices.payload.error);

// BLE discovery usually returns id + name first, then you can fetch device_id via getFeatures.
const pickedDevice = availableDevices.payload[0];
const connectId = pickedDevice.connectId;

const featureInfo = await ukeySdk.getFeatures(connectId);
if (!featureInfo.success) throw new Error(featureInfo.payload.error);
const deviceId = featureInfo.payload.device_id;

After resolving them, keep connectId and deviceId in the current session because most chain methods need both values.

First call example (BTC address)

const opResult = await ukeySdk.btcGetAddress(connectId, deviceId, {
path: "m/84'/0'/2'/0/0",
coin: "btc",
showOnUKey: false,
});

if (opResult.success) {
console.log("Resolved BTC address:", opResult.payload.address);
} else {
console.error("Request failed:", opResult.payload.error, opResult.payload.code);
}

hint

  • Subscribe to UI_EVENT as early as possible to avoid requests getting stuck due to no one responding to PIN, Passphrase or confirmation events. See Configure events.
  • BLE behavior strongly depends on the system version, permission status and real device environment. Please verify on the real device first.

continue