A comprehensive Unity plugin for Bluetooth Low Energy (BLE) communication on iOS devices using the CoreBluetooth framework.
- ✅ Auto-Initialization: Automatically initializes before scene load - no manual setup required
- ✅ Smart GameObject Management: Automatically finds or creates "BluetoothManager" GameObject
- ✅ Device Discovery: Scan for BLE devices with comprehensive advertisement data
- ✅ Connection Management: Connect and disconnect from BLE devices
- ✅ Device Information: Access RSSI, service UUIDs, manufacturer data, and more
- ✅ Event-Driven Architecture: Subscribe to discovery, connection, and state change events
- ✅ Connected Device Tracking: Manage multiple connected devices simultaneously
- ✅ GATT Characteristic Operations: Write data, subscribe/unsubscribe to notifications
- ✅ Service Discovery: Discover and access all services and characteristics
- ✅ iOS CoreBluetooth Integration: Native iOS implementation for optimal performance
- ✅ Editor Simulation: Test your BLE logic in the Unity Editor
- ✅ Comprehensive Error Handling: Robust error handling and logging
- Go to the GitHub repository releases page
- Download the latest
UnityBLE2IOS.unitypackagefile - In Unity, go to Assets → Import Package → Custom Package...
- Select the downloaded
.unitypackagefile - Click Import to add the plugin to your project
- Open Unity Package Manager (Window → Package Manager)
- Click the "+" button in the top-left corner
- Select "Add package from git URL..."
- Enter:
https://github.com/daiyk/UnityBLE2IOS.git - Click "Add"
- Unity: 2022.3 or later
- iOS: 10.0 or later
- Xcode: 12 or later
- Platform: iOS only (uses CoreBluetooth framework)
⚠️ Note: The sample scene requires Unity 6.0 or later to run properly. The core plugin works with Unity 2022.3+, but the sample scene uses TextMeshPro font assets that are not compatible between Unity 6.0 and lower versions.
The plugin provides mock BLE devices and simulated connection behavior in the Unity Editor to allow you to develop and test your UI and application logic without needing an iOS device. Real Bluetooth operations only work when deployed to an actual iOS device.
The BluetoothManager automatically initializes when your app starts using Unity's RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad). This means:
- ✅ No manual initialization required - Just access
BluetoothManager.Instance - ✅ Smart GameObject management - Automatically looks for existing "BluetoothManager" GameObject or creates one
- ✅ Persistent across scenes - The manager persists throughout your app lifecycle
- ✅ Component auto-attachment - Automatically adds BluetoothManager component if missing
The plugin follows this logic:
- Looks for existing GameObject named "BluetoothManager" in the scene
- Adds component if missing - Attaches BluetoothManager component if GameObject exists but component is missing
- Creates new GameObject - If no "BluetoothManager" GameObject found, creates one automatically
- Persists across scenes - Uses
DontDestroyOnLoad()to maintain the GameObject
// Start scanning for devices
BluetoothManager.Instance.StartScanning();
// Request permissions if needed
BluetoothManager.Instance.RequestPermissions();
// Connect to a device
BluetoothManager.Instance.ConnectToDevice(deviceId);
// Wait for OnServicesDiscovered before interacting with characteristics
if (BluetoothManager.Instance.IsGattReady(deviceId))
{
BluetoothManager.Instance.SubscribeToCharacteristic(deviceId, characteristicUUID);
}
// Then, in OnCharacteristicNotificationStateChanged after isNotifying == true:
BluetoothManager.Instance.WriteCharacteristic(deviceId, characteristicUUID, data);using UnityBLE2IOS;
using UnityEngine;
public class BLEController : MonoBehaviour
{
void Start()
{
var bluetoothManager = BluetoothManager.Instance;
// Subscribe to events
bluetoothManager.OnDeviceDiscovered += OnDeviceFound;
bluetoothManager.OnDeviceConnected += OnDeviceConnected;
bluetoothManager.OnServicesDiscovered += OnServicesDiscovered;
bluetoothManager.OnDeviceDisconnected += OnDeviceDisconnected;
bluetoothManager.OnPermissionResult += OnPermissionResult;
bluetoothManager.OnCharacteristicValueReceived += OnCharacteristicValue;
bluetoothManager.OnCharacteristicNotificationStateChanged += OnNotificationStateChanged;
// Request permissions (optional - auto-requested on first use)
bluetoothManager.RequestPermissions();
}
private void OnPermissionResult(bool granted)
{
if (granted)
{
Debug.Log("Bluetooth permission granted - starting scan");
BluetoothManager.Instance.StartScanning();
}
else
{
Debug.LogError("Bluetooth permission denied");
}
}
private void OnDeviceFound(BluetoothDevice device)
{
Debug.Log($"Found device: {device.name} (RSSI: {device.rssi})");
Debug.Log($"Services: {string.Join(", ", device.serviceUUIDs)}");
// Connect to the first device found (example)
BluetoothManager.Instance.ConnectToDevice(device.deviceId);
}
private void OnDeviceConnected(string deviceId)
{
Debug.Log($"Connected to device: {deviceId}");
Debug.Log("Waiting for GATT discovery to complete before using characteristics");
}
private void OnServicesDiscovered(string deviceId)
{
var services = BluetoothManager.Instance.GetDeviceServices(deviceId);
var characteristics = BluetoothManager.Instance.GetDeviceCharacteristics(deviceId);
Debug.Log($"GATT ready. Device has {services.Length} services and {characteristics.Length} characteristics");
foreach (var characteristic in characteristics)
{
if (characteristic.CanNotify())
{
BluetoothManager.Instance.SubscribeToCharacteristic(deviceId, characteristic.characteristicUUID);
break;
}
}
}
private void OnNotificationStateChanged(CharacteristicNotificationStateResult result)
{
if (result.IsError())
{
Debug.LogError($"Notification state update failed: {result.error}");
return;
}
Debug.Log($"Notifications {(result.isNotifying ? "enabled" : "disabled")} for {result.characteristicUUID}");
// Safe point for protocols that respond immediately after a command write.
if (result.isNotifying)
{
// BluetoothManager.Instance.WriteCharacteristic(result.deviceId, commandCharacteristicUUID, commandData);
}
}
private void OnCharacteristicValue(CharacteristicValueMessage message)
{
Debug.Log($"Received data from {message.characteristicUUID}: {message.data}");
// Convert hex data to bytes if needed
byte[] bytes = message.GetDataAsBytes();
string text = message.GetDataAsString();
}
private void OnDeviceDisconnected(string deviceId)
{
Debug.Log($"Disconnected from device: {deviceId}");
}
}RequestPermissions()- Request Bluetooth permissions from userStartScanning()- Start scanning for BLE devicesStopScanning()- Stop scanning for BLE devicesConnectToDevice(string deviceId)- Connect to a specific deviceDisconnectDevice(string deviceId)- Disconnect from a deviceDisconnectAllDevices()- Disconnect from all connected devicesIsGattReady(string deviceId)- Check whether services and characteristics have been discovered for a device
WriteCharacteristic(string deviceId, string characteristicUUID, byte[] data)- Write byte data to characteristicWriteCharacteristic(string deviceId, string characteristicUUID, string hexData)- Write hex string to characteristicSubscribeToCharacteristic(string deviceId, string characteristicUUID)- Subscribe to characteristic notificationsUnsubscribeFromCharacteristic(string deviceId, string characteristicUUID)- Unsubscribe from notifications
GetDeviceServices(string deviceId)- Get all services for a connected deviceGetDeviceCharacteristics(string deviceId)- Get all characteristics for a deviceGetServiceCharacteristics(string deviceId, string serviceUUID)- Get characteristics for specific service
GetDiscoveredDevices()- Get list of all discovered devicesGetConnectedDevices()- Get list of all connected devicesGetConnectedDevice(string deviceId)- Get specific connected deviceGetDiscoveredDevice(string deviceId)- Get specific discovered deviceIsDeviceConnected(string deviceId)- Check if device is connectedIsDeviceDiscovered(string deviceId)- Check if device was discoveredClearDiscoveredDevices()- Clear the discovered devices listGetDiscoveredDeviceByIndex(int index)- Get discovered device by index from native layer
IsBluetoothEnabled()- Check if Bluetooth is enabledGetConnectionStatus()- Get comprehensive status summaryGetConnectedDeviceCount()- Get number of connected devicesGetDiscoveredDeviceCount()- Get number of discovered devices
OnBluetoothStateChanged- Bluetooth enabled/disabledOnDeviceDiscovered- New device discoveredOnDeviceConnected- Device link establishedOnServicesDiscovered- Services and characteristics discovered; GATT operations are now safeOnDeviceDisconnected- Device disconnectedOnConnectionFailed- Connection attempt failedOnPermissionResult- Bluetooth permission resultOnCharacteristicValueReceived- Data received from characteristicOnCharacteristicWriteSuccess- Characteristic write completed successfullyOnCharacteristicWriteError- Characteristic write failedOnCharacteristicNotificationStateChanged- Notification state changed or failed
Properties available for each discovered/connected device:
public class BluetoothDevice
{
public string deviceId; // Unique device identifier
public string name; // Device name
public int rssi; // Signal strength
public bool isConnectable; // Whether device accepts connections
public string[] serviceUUIDs; // Advertised service UUIDs
public string manufacturerData; // Manufacturer-specific data (hex string)
public string localName; // Local name from advertisement
public int txPowerLevel; // Transmission power level
}public class BluetoothCharacteristic
{
public string serviceUUID; // Parent service UUID
public string characteristicUUID; // Characteristic UUID
public string[] properties; // Available operations (read, write, notify, etc.)
public bool isNotifying; // Current notification state
// Helper methods
public bool CanRead() { ... } // Check if characteristic supports reading
public bool CanWrite() { ... } // Check if characteristic supports writing
public bool CanNotify() { ... } // Check if characteristic supports notifications
}public class CharacteristicValueMessage
{
public string deviceId; // Source device ID
public string characteristicUUID; // Characteristic UUID
public string data; // Raw hex data
// Helper methods
public byte[] GetDataAsBytes() { ... } // Convert hex to byte array
public string GetDataAsString() { ... } // Convert hex to UTF-8 string
}public class CharacteristicNotificationStateResult
{
public string deviceId; // Source device ID
public string characteristicUUID; // Characteristic UUID
public bool isNotifying; // Whether notifications are currently active
public string error; // Error text, if any
}private string writableCharacteristicUUID;
private readonly byte[] commandData = { 0x01, 0x02, 0x03 };
void OnServicesDiscovered(string deviceId)
{
var characteristics = BluetoothManager.Instance.GetDeviceCharacteristics(deviceId);
foreach (var characteristic in characteristics)
{
Debug.Log($"Found characteristic: {characteristic.characteristicUUID}");
Debug.Log($"Properties: {string.Join(", ", characteristic.properties)}");
// Subscribe to notifications if supported
if (characteristic.CanNotify())
{
BluetoothManager.Instance.SubscribeToCharacteristic(deviceId, characteristic.characteristicUUID);
}
// Remember a writable characteristic, but wait for notifications to be active before sending a command.
if (characteristic.CanWrite())
{
writableCharacteristicUUID = characteristic.characteristicUUID;
}
}
}
void OnCharacteristicNotificationStateChanged(CharacteristicNotificationStateResult result)
{
if (result.IsError())
{
Debug.LogError(result.error);
return;
}
Debug.Log($"Notification state for {result.characteristicUUID}: {result.isNotifying}");
if (result.isNotifying && !string.IsNullOrEmpty(writableCharacteristicUUID))
{
BluetoothManager.Instance.WriteCharacteristic(result.deviceId, writableCharacteristicUUID, commandData);
}
}
void OnCharacteristicValueReceived(CharacteristicValueMessage message)
{
Debug.Log($"Data from {message.characteristicUUID}: {message.data}");
// Process the received data
byte[] bytes = message.GetDataAsBytes();
// ... handle your device-specific protocol
}The package includes a comprehensive sample:
A complete BLE sample scene with full UI implementation:
- BLEStatusController.cs - Main controller with comprehensive BLE management
- BLEDeviceItem.cs - Interactive device list item component
- UnityBLESample.unity - Complete sample scene
- BLE_Device.prefab - Device list item prefab
Features:
- Interactive device scanning and connection
- Real-time device list with RSSI updates
- Debug console with operation logging
- Connection status management
- Visual feedback for device selection
Import the sample through Package Manager to see a complete usage example.
The plugin automatically configures required iOS settings, but ensure:
-
Info.plist includes Bluetooth usage descriptions:
<key>NSBluetoothAlwaysUsageDescription</key> <string>This app uses Bluetooth to connect to nearby devices</string> <key>NSBluetoothPeripheralUsageDescription</key> <string>This app uses Bluetooth to connect to nearby devices</string>
-
Minimum iOS Version: Set to iOS 10.0 or later
- No devices found: Ensure Bluetooth is enabled and app has permission
- Connection fails: Check device is in range and connectable
- Build errors: Verify iOS deployment target is 10.0+
- "Object BluetoothManager not found" error: The GameObject is auto-created, but ensure you're accessing
BluetoothManager.Instancebefore native callbacks occur
If you want to manually place a "BluetoothManager" GameObject in your scene:
- Create an empty GameObject in your scene
- Name it exactly "BluetoothManager"
- The plugin will automatically attach the BluetoothManager component
- The GameObject will persist across scene changes automatically
void Start()
{
// Force GameObject creation and verify setup
var manager = BluetoothManager.Instance;
// Check if properly initialized
Debug.Log($"Bluetooth enabled: {manager.IsBluetoothEnabled()}");
Debug.Log($"Manager GameObject: {manager.gameObject.name}");
}Enable verbose logging to troubleshoot issues:
// Check connection status
Debug.Log(BluetoothManager.Instance.GetConnectionStatus());
// Monitor discovered devices
foreach(var device in BluetoothManager.Instance.GetDiscoveredDevices())
{
Debug.Log($"Device: {device.name}, RSSI: {device.rssi}");
}MIT License - see LICENSE file for details.
For issues, feature requests, or contributions, please visit the GitHub repository.