react-native-ble-nitro

repository·main·Indexed 41 days ago

https://github.com/zykeco/react-native-ble-nitro

A high-performance Bluetooth Low Energy (BLE) library for React Native, version 1.15.1, built using Nitro Modules and JSI for zero-overhead native communication. It supports scanning, connecting, reading/writing characteristics, and notifications. Features include an Expo config plugin, iOS state restoration via BleNitroManager, and Android-specific MTU and connection priority management.

Tokens
10.2K
Snippets
39
Records
43
Agent score
99%

What's inside react-native-ble-nitro

  1. How the Nitro Modules architecture works

    main

    The library is built on the Nitro Modules foundation, which provides several key advantages for BLE operations:

    • Direct JSI Communication: Eliminates the React Native bridge overhead for faster communication.
    • Type-Safe Bindings: Ensures compile-time type checking across the JavaScript and Native boundaries.
    • High Performance: Provides near-native performance for all BLE operations.
    • Memory Efficient: Uses smart references for optimal memory management.

    Platform implementations are native: Swift for iOS (using Core Bluetooth) and Kotlin for Android (using Android BLE APIs), with shared logic handled via C++.

  2. Handle Android BLE Permissions

    main

    Android requires specific permissions depending on the API level. For Android 12+ (API 31+), you must request BLUETOOTH_SCAN, BLUETOOTH_CONNECT, and ACCESS_FINE_LOCATION. For older versions, ACCESS_FINE_LOCATION is the primary requirement for scanning.

    Below is the recommended pattern for requesting these permissions in a React Native application:

    import { PermissionsAndroid, Platform } from 'react-native';
    
    const requestPermissionsAndroid = async () => {
      if (Platform.OS !== 'android') {
        return true
      }
      if (Platform.OS === 'android' && PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION) {
        const apiLevel = parseInt(Platform.Version.toString(), 10);
        if (apiLevel < 31) {
          const result = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION);
          return (
            result === PermissionsAndroid.RESULTS.GRANTED
          );
        }
        if (PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN && PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT) {
          const result = await PermissionsAndroid.requestMultiple([
            PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
            PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
            PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
          ])
    
          return (
            result['android.permission.BLUETOOTH_CONNECT'] === PermissionsAndroid.RESULTS.GRANTED &&
            result['android.permission.BLUETOOTH_SCAN'] === PermissionsAndroid.RESULTS.GRANTED &&
            result['android.permission.ACCESS_FINE_LOCATION'] === PermissionsAndroid.RESULTS.GRANTED
          )
        }
    
        throw new Error('Request permissions failed');
      }
    };
    
    const hasPermissions = await requestPermissionsAndroid();
  3. Initialize BleNitro

    main

    You can use the library via a singleton instance or by creating a custom BleNitroManager.

    Use BleNitro.instance() for standard usage.

    Use new BleNitroManager() if you need to implement iOS state restoration. To enable state restoration, you must provide a unique restoreIdentifier and an onRestoredState callback. Note that the singleton instance does not support state restoration by default.

    import { BleNitro, BleNitroManager } from 'react-native-ble-nitro';
    
    // Standard singleton
    const ble = BleNitro.instance();
    
    // Custom instance for iOS state restoration
    const ble = new BleNitroManager({
      restoreIdentifier: 'my-unique-identifier',
      onRestoredState: (peripherals) => {
        console.log('Restored peripherals:', peripherals);
      },
    });
  4. Use Lazy Initialization on iOS

    main

    By default, if iOSLazyInit is enabled, BLE will only initialize when the first API call (like startScan() or connect()) is made, which triggers the permission dialog. To explicitly initialize BLE ahead of time and trigger the permission dialog manually, call iosLazyInit().

    To enable this behavior in a React Native CLI project, add the following key to your Info.plist:

    <key>BLENitroLazyInit</key>
    <true/>
    bleNitro.iosLazyInit();
  5. Configure react-native-ble-nitro for Expo

    main

    For Expo projects, use the built-in config plugin by adding react-native-ble-nitro to your app.json or app.config.js. You can configure background capabilities, operation modes, and the Bluetooth permission string.

    Available configuration options:

    • isBackgroundEnabled: Boolean to enable background BLE support.
    • modes: Array of modes (e.g., ["peripheral", "central"]).
    • bluetoothAlwaysPermission: The string used for the iOS Bluetooth permission prompt.
    {
      "expo": {
        "plugins": [
          [
            "react-native-ble-nitro",
            {
              "isBackgroundEnabled": true,
              "modes": ["peripheral", "central"],
              "bluetoothAlwaysPermission": "Allow $(PRODUCT_NAME) to connect to bluetooth devices"
            }
          ]
        ]
      }
    }
  6. Run the React-Native Ble-Nitro Example project

    main

    To run the example project provided in the repository, you must first pack the main library into a .tgz file, install that local package into the example directory, and then run the application on a physical device. Note that BLE (Bluetooth Low Energy) functionality typically requires a real device rather than a simulator/emulator.

    # 1. Pack the library in the parent directory
    cd ../ && npm pack
    
    # 2. Install the generated .tgz in the example directory
    cd ./example && npm install ../react-native-ble-nitro/react-native-ble-nitro-*.tgz
    npm install
    
    # 3. Run on a real device
    npm run ios -- --device "Your Device Name"
    npm run android
  7. Configure the Expo Plugin for react-native-ble-nitro

    main

    When using Expo, you can configure the BLE module via the Expo Config Plugin. Use the BleNitroPluginProps interface to manage background modes, permissions, and initialization behavior.

    Available options:

    • isBackgroundEnabled: Enable background BLE support.
    • neverForLocation: Assert no location derivation (useful for Android 12+).
    • modes: Array of iOS background modes: 'peripheral' or 'central'.
    • bluetoothAlwaysPermission: Custom iOS permission message.
    • androidAdvertisingEnabled: Enables Android Peripheral mode (advertising).
    • iOSLazyInit: If true, delays BLE initialization to prevent the permission dialog from appearing immediately on app launch.
    interface BleNitroPluginProps {
      isBackgroundEnabled?: boolean;
      neverForLocation?: boolean;
      modes?: ('peripheral' | 'central')[];
      bluetoothAlwaysPermission?: string | false;
      androidAdvertisingEnabled?: boolean;
      iOSLazyInit?: boolean;
    }
  8. Configure iOS Background Modes

    main

    To allow your app to act as a BLE peripheral or central while in the background on iOS, configure the modes option in your plugin configuration. This adds the following keys to your Info.plist:

    • bluetooth-peripheral: Act as BLE peripheral in background.
    • bluetooth-central: Scan/connect as central in background.
    {
      "modes": ["peripheral", "central"]
    }
  9. Configure Expo with the withBleNitro plugin

    main

    To use react-native-ble-nitro in an Expo project, use the withBleNitro config plugin in your app.config.ts or app.json. This plugin automates the addition of Bluetooth permissions and background modes for both Android and iOS.

    Configuration Options

    Android Options

    • isBackgroundEnabled (boolean, default: false): Enables background BLE support by adding required permissions and setting the android.hardware.bluetooth_le feature to required=true in the AndroidManifest.xml.
    • neverForLocation (boolean, default: false): If true, configures BLUETOOTH_SCAN with the neverForLocation flag and limits ACCESS_FINE_LOCATION to SDK versions $\le$ 30. Use this only if your app does not derive physical location from Bluetooth scans.
    • androidAdvertisingEnabled (boolean, default: false): Enables the android.permission.BLUETOOTH_ADVERTISE permission.

    iOS Options

    • modes (('peripheral' | 'central')[], default: undefined): Specifies iOS background modes for BLE operations (e.g., ['central'] adds bluetooth-central to UIBackgroundModes).
    • bluetoothAlwaysPermission (string | false, default: "Allow $(PRODUCT_NAME) to connect to bluetooth devices"): The message shown to users for the NSBluetoothAlwaysUsageDescription permission. Set to false to skip adding this permission.
    • iOSLazyInit (boolean, default: false): Enables lazy-initialization for CBCentralManager. This prevents the Bluetooth permission dialog from appearing immediately at app launch, delaying it until the first BLE API call is made.
    import { withBleNitro } from 'react-native-ble-nitro';
    
    export default {
      expo: {
        plugins: [
          [
            'react-native-ble-nitro',
            {
              isBackgroundEnabled: true,
              neverForLocation: true,
              modes: ['central'],
              bluetoothAlwaysPermission: 'Custom permission message',
              iOSLazyInit: true
            }
          ]
        ]
      }
    };