2

On iOS, I only can check state of Bluetooth. I'm find the solutions on network and use it.

public class CallBluetoothIphoneService : ICallBlueTooth
{
    public void LaunchBluetoothOnPhone()
    {
        try
        {
            // Is bluetooth enabled?
            var bluetoothManager = new CBCentralManager();
            if (bluetoothManager.State == CBCentralManagerState.PoweredOff|| bluetoothManager.State == CBCentralManagerState.Unknown)
                // Does not go directly to bluetooth on every OS version though, but opens the Settings on most
                UIApplication.SharedApplication.OpenUrl(new NSUrl("App-Prefs:root=Bluetooth"));
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            throw;
        }
    }
}

But when I try turn off Bluetooth and test code, state of bluetooth is "Unknown". Then I run code, device open settings, toggle button has color green (turn on bluetooth), but when I check state of Bluetooth in code, State of Bluetooth is "Unknown", is not "Power on".

I'm using Xamarin 3.3 and test on device iOS version 12.0.

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Huu Bao Nguyen
  • 1,051
  • 2
  • 14
  • 40

1 Answers1

2

I am not sure exactly what you want to do, but if your intent is to open the Bluetooth settings page, this:

UIApplication.SharedApplication.OpenUrl(new NSUrl("App-Prefs:root=Bluetooth"));

won't work. Apple has at some points allowed this (iOS 8 IIRC) and at other points it has disallowed this (most versions of iOS). See this long SO thread about this issue: How to open Settings programmatically like in Facebook app?

Regardless, there is no need. When iOS detects that your app has created a CBCentralManager type with delegate, iOS will display an alert to the user that allows them to go to the bluetooth settings to enable bluetooth by tapping the "Settings" button in the alert.

As far as always getting state as "Unknown", you need to check the state in the delegate for the CBCentralManager. You cannot use the parameterless CBCentralManager constructor new CBCentralManager();. Check the apple docs: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager?language=objc and note that there are only two listed init methods, one that takes delegate and queue parameters, and one that takes delegate, queue, and options parameters, although no one complains if you use the parameterless constructor... but you will never get the correct state if you use it. See: https://stackoverflow.com/a/36824770/2913599

So try this:

public class CallBluetoothIphoneService : ICallBluetooth
{
    public void LaunchBluetoothOnPhone()
    {
        try
        {
            // Is bluetooth enabled?
            var bluetoothManager = new CBCentralManager(new MySimpleCBCentralManagerDelegate(), DispatchQueue.CurrentQueue);

            // This will always show state "Unknown". You need to check it in the delegate's UpdatedState method
            Console.WriteLine($"State: {bluetoothManager.State.ToString()}");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}

public class MySimpleCBCentralManagerDelegate : CBCentralManagerDelegate
{
    override public void UpdatedState(CBCentralManager mgr)
    {
        // You can check the state in this delegate method
        Console.WriteLine($"UpdatedState: {mgr.State.ToString()}");

        if (mgr.State == CBCentralManagerState.PoweredOn)
        {
            //Passing in null scans for all peripherals. Peripherals can be targeted by using CBUIIDs
            CBUUID[] cbuuids = null;
            mgr.ScanForPeripherals(cbuuids); //Initiates async calls of DiscoveredPeripheral
            //Timeout after 30 seconds
            var timer = new Timer(30 * 1000);
            timer.Elapsed += (sender, e) => mgr.StopScan();
        }
        else
        {
            //Invalid state -- Bluetooth powered down, unavailable, etc.
            System.Console.WriteLine("Bluetooth is not available");
        }
    }

    public override void DiscoveredPeripheral(CBCentralManager central, CBPeripheral peripheral, NSDictionary advertisementData, NSNumber RSSI)
    {
        Console.WriteLine("Discovered {0}, data {1}, RSSI {2}", peripheral.Name, advertisementData, RSSI);
    }
}

Bottom line: always create a CBCentralManager object with one of the following constructors:

CBCentralManager(ICBCentralManagerDelegate, DispatchQueue)

CBCentralManager(ICBCentralManagerDelegate, DispatchQueue, CBCentralInitOptions)
jgoldberger - MSFT
  • 5,978
  • 2
  • 20
  • 44