Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Bluetooth CBCentralManager always reports state as Unknown on Mac

Tags:

I'm new to Swift and Mac/iOs programming in general. I'm running this sample on a Macbook Pro with BLE support and with Bluetooth turned on, using Xcode 7.3.

import Foundation
import CoreBluetooth

func printState(state: CBCentralManagerState) {
    switch state {
    case CBCentralManagerState.PoweredOn:
        print("Powered on")
    case CBCentralManagerState.PoweredOff:
        print("Powered off")
    case CBCentralManagerState.Resetting:
        print("Resetting")
    case CBCentralManagerState.Unauthorized:
        print("Unauthorized")
    case CBCentralManagerState.Unknown:
        print("Unknown")
    default:
        print ("Unsupported")
    }

}

var myCentralManager = CBCentralManager(delegate:nil, queue:nil)
while true {
    printState(myCentralManager.state)
    sleep(1)
}

The code prints out "Unknown" over and over, even after many minutes. I've also tried setting up a delegate but the didUpdateState callback doesn't called. I've also tried to run this from the command line and Swift interpreter and get the same result.

I must be missing something very basic. How can I get it to report that the CBCentralManager is powered on?

like image 884
NNS Avatar asked Apr 23 '16 19:04

NNS


1 Answers

Near as I can tell, if you don't use one of the two initializers listed in the documentation, this is the result -- you want to use either:

init(delegate:queue:)

or:

init(delegate:queue:options:)

Xcode doesn't complain if you just call:

CBCentralManager()

but if you do that, it seems like you'll stay in CBCentralManagerState.Unknown indefinitely.

If you want to experiment, make a playground and paste this code, and try it with the two initializer variants I've listed:

import Cocoa
import CoreBluetooth
import XCPlayground

@objc
public class BluetoothWatcher: NSObject, CBCentralManagerDelegate {
    var cbcm: CBCentralManager!;
    var timer: NSTimer!;

    override init() {
        super.init();

        /*:
            When you initialize a central manager, the delegate seems important. If you comment out the initializer with the delegate and uncomment the other one, it'll stay in state unknown indefinitely.
        */
        //cbcm = CBCentralManager(delegate: self, queue:nil);
        cbcm = CBCentralManager();

        checkStateInOneSecond();
    }

    public func centralManagerDidUpdateState(central: CBCentralManager) {
        print( "State updated: \(stateString())" );
    }

    func checkStateInOneSecond() {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), {
            self.checkState();
        });
    }

    public func checkState() {
        print( "Timer fired, state: \(stateString())" );
        checkStateInOneSecond();
    }

    func stateString() -> String {
        switch(cbcm.state) {
        case .Resetting:
            return "resetting"
        case .PoweredOn:
            return "powered on";
        case .PoweredOff:
            return "powered off";
        case .Unknown:
            return "unknown";
        case .Unsupported:
            return "unsupported";
        case .Unauthorized:
            return "unauthorized";
        }
    }
}


XCPlaygroundPage.currentPage.needsIndefiniteExecution = true;
var watcher = BluetoothWatcher();

You'll see with the delegate, it starts in Unknown, and almost immediately the delegate gets called with a state change to PoweredOn, and every subsequent timer fire shows PoweredOn.

Do the same thing without the delegate, with the empty initializer, and the delegate method will never be called (no surprise) and the timer fires will continue to show Unknown.

like image 68
Geoffrey Wiseman Avatar answered Sep 28 '22 04:09

Geoffrey Wiseman