1

I want to create a method startCentralManager that waits until the delegate method centralManagerDidUpdateState is called and returns a boolean value depending on the central state.

I want to use the method like this:

let centralManager = CentralManager()
let ready = centralManager.startCentralManager()

The CentralManager class should look something like this

class CentralManager: NSObject, CBCentralManagerDelegate {
   var centralManager: CBCentralManager?

   func startCentralManager() -> Bool {
      centralManager = CBCentralManager.init(delegate: self, queue: nil)
   }

   func centralManagerDidUpdateState(central: CBCentralManager) {
      // startCentralManager() should return this
      return central.state == CBCentralManagerState.PoweredOn
   }
}

How can I solve this?

Thank you very much, in advance.

UPDATE:

There are some other methods, but the situation is almost equal. Another method is the following:

let connected = centralManager.connect<PARAMS>)

In this method if have to call

func scanForPeripheralsWithServices(_ serviceUUIDs: [CBUUID]?, options options: [String : AnyObject]?)

and wait until the delegate method

func centralManager(_ central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData advertisementData: [String : AnyObject], RSSI RSSI: NSNumber)

is called. Here, I have to call

func connectPeripheral(_ peripheral: CBPeripheral, options options: [String : AnyObject]?)

and again wait until the delegate method

func centralManager(_ central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral)

is called. In case of success, the call to centralManager.connect(<PARAMS>) should return true.

neugebap
  • 19
  • 3
  • Are your functions backwards? `startCentralManager` says it will return a `Bool`, but it doesn't return anything. `centralManagerDidUpdateState` says it won't return anything, but it tries to return a `Bool`. – Aaron Rasmussen Jan 14 '16 at 21:40
  • Exactly this is my problem. I want the `startCentralManager` method to return the `Bool` from the delegate. – neugebap Jan 14 '16 at 21:58
  • Should `centralManagerDidUpdateState` be returning a `Bool`? – Aaron Rasmussen Jan 14 '16 at 22:18
  • No, `centralManagerDidUpdateState ` should not return a `Bool`. Please see my answer below to clarify this. – neugebap Jan 15 '16 at 19:26

3 Answers3

0

CBCentralManager works asychronously and you get informed of its status through a call to your delegate at some point in time that you have no direct control over.

You can't wait for asynchronous response between two lines of code. You'll have to design your program differently.

If you have a view controller that depends on the readiness of the CBCentralManager, you can make it the delegate and enable its peripheral related controls when your centralManagerDidUpdateState function is called.

for example (if you're using this for a scanner) :

class ScanningViewController:UIViewController,CBCentralManagerDelegate
{
   var centralManager: CBCentralManager? = nil
   override viewDidLoad()
   {
      centralManager = CBCentralManager.init(delegate: self, queue: nil)
   }

   func centralManagerDidUpdateState(central: CBCentralManager) 
   {
     if central.state == CBCentralManagerState.PoweredOn
     {
       // enable scanning controls here
     }
   }
} 
Alain T.
  • 40,517
  • 4
  • 31
  • 51
0

I found a solution:

class CentralManager: NSObject, CBCentralManagerDelegate {
   var centralManager: CBCentralManager?
   var ready: Bool = false

   func startCentralManager() -> Bool {
      centralManager = CBCentralManager.init(delegate: self, queue: nil)

      ready = false
      while ready == false {
         NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture())
      }

      return ready
   }

   func centralManagerDidUpdateState(central: CBCentralManager) {
      ready = central.state == CBCentralManagerState.PoweredOn
   }
}

Is this a good programming practice? Is there a better solution?

Thank you.

neugebap
  • 19
  • 3
  • 1
    It's a pretty bad practice actually. You're taking over the runLoop, probably on the main thread. That's sure to have unpredictable consequences for the rest of the app (but may not be a problem until you start adding more functionality). What you're trying to do goes against the event driven and division of responsibility concepts of the iOS frameworks and object oriented programming in general. You need to adjust your way of designing programs (see the solution I posted below). – Alain T. Jan 16 '16 at 01:18
  • The problem is that I have to call the method `startCentralManager` from a third party library I have no control. The third party library expects the central manager to be ready after the method has finished. That is why I try to "synchronize" this asynchronous delegate pattern. Using the runLoop is a pretty bad practice - thank you for your answer Alain T. - but what is the least worst solution in my case? – neugebap Jan 17 '16 at 10:04
  • I would need more information on the various objects/delegates that you are trying to synchronize and in which order you need things to happen. I have a couple of techniques to coordinate asynchronous events but they depend greatly on what's available as callbacks or completion block – Alain T. Jan 17 '16 at 13:15
  • I have updated to original question to provide some more information. Do you have any advise how I can "synchronize" this asynchronous events? – neugebap Jan 19 '16 at 07:06
0

Create a timer that fires every X seconds

Inside the timer's selector check if connected and invalidate the timer or let it keep checking if not connected

Or consider using a semaphore and a dispatch_wait_forever

Jeff Brown
  • 86
  • 8