Xamarin Bluetooth LE: Scan for devices iOS Edition

Posted on Leave a commentPosted in Uncategorized

For Android see Xamarin Bluetooth LE: Scan for devices Android Edition

Proceeding with iOS and create the same “scan for devices” app. This time I am skipping the boilerplate code of buttons and events and such. Just cut to the chase with the BluetoothEnumerationService

    public class BluetoothEnumerationService
    {
        private readonly CBCentralManager _centralManager;
        private readonly Dictionary<string, CBPeripheral> _devices = new Dictionary<string, CBPeripheral>();

        public BluetoothEnumerationService()
        {
            _centralManager = new CBCentralManager(new MyCentralManagerDelegate(_devices), DispatchQueue.CurrentQueue);    
        }

        public async Task<IEnumerable<CBPeripheral>> GetDevicesAsync()
        {
            if (_centralManager.State != CBCentralManagerState.PoweredOn)
                return Enumerable.Empty<CBPeripheral>();

            _centralManager.ScanForPeripherals((CBUUID[])null); // #NoFilter
            await Task.Delay(200);
            _centralManager.StopScan();
            return _devices.Values;
        }
    }

Simple stuff here. You need a new instance of CBCentralManager. The MyCentralManagerDelegate is what’s actually going to give you your devices (and it’s coming up). Like before, I just want to check if the Bluetooth features are turned on and if not, return an empty enumeration of CBPeripheral.

Then just start/wait/stop the scan and through the magic of MyCentralManagerDelegate, _devices.Values is going to have CBPeripherals in it if they were found during scan.

If you were going to do any filtering of devices, that’s on line 16 above.

    public class MyCentralManagerDelegate : CBCentralManagerDelegate
    {
        private readonly Dictionary<string, CBPeripheral> _devices;

        public MyCentralManagerDelegate(Dictionary<string, CBPeripheral> devices)
        {
            _devices = devices;
        }

        public override void DiscoveredPeripheral(CBCentralManager central, CBPeripheral peripheral, NSDictionary advertisementData, NSNumber receivedSignalStrengthIndication)
        {
            _devices[peripheral.Identifier.AsString()] = peripheral;
        }

        public override void UpdatedState(CBCentralManager cbCentralManager)
        {
            // Here you can perform actions when the bluetooth state changes on/off/resetting...
        }
    }

You could probably imagine what the MyCentralManagerDelegate class was for before it was written, but there it’s spelled out. You get a call to DiscoveredPeripheral when a new peripheral is discovered. I’m using the same dictionary scheme to make sure I get unique devices only in my lists.

For context the ViewController is here:

    public partial class ViewController : UIViewController
    {
        private readonly BluetoothEnumerationService _bluetoothEnumerationService;

        public ViewController(IntPtr handle) : base(handle)
        {
            _bluetoothEnumerationService = new BluetoothEnumerationService();
        }

        async partial void startScanning(UIButton sender)
        {
            var devices = await _bluetoothEnumerationService.GetDevicesAsync();
            bleDeviceList.Source = new TableSource(devices.Select(x => x.Name).ToList());
            bleDeviceList.ReloadData();
        }
    }