5

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?

NNS
  • 93
  • 1
  • 4

1 Answers1

5

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.

Geoffrey Wiseman
  • 5,459
  • 3
  • 34
  • 52
  • Got it working with the delegate. I had tried this before but it wasn't working because I didn't put in a call to dispatch_main – NNS Apr 26 '16 at 20:30
  • 1
    This is absolutely correct. Thanks for saving me hours more of banging my head against the desk. – davidgyoung Jun 27 '17 at 18:07