1

I am trying to make some wrapper functions to handle some asynchronous BLE CoreBluetooth calls synchronously using closures. I want my run function to be blocked and not exit until receiving the completionHandler callback. However, if I call my run function multiple times in a loop, it does not wait for the completionHandler callback and calls run again. So in the end the callback is only received for the last run.

How can I modify my code so that it will wait in the run function until completionHandler() is called in the registerDidUpdateCallback function?

I have tried reading a bunch of articles about closures, but I am still confused and my brain is fried.

func run(characteristic: CBCharacteristic, completionHandler: @escaping 
    CommandCompletionHandler) {
        registerDidUpdateCallback(completionHandler)
        motePeripheral.basePeripheral?.readValue(for: characteristic)
 }

func registerDidUpdateCallback(completionHandler: @escaping CommandCompletionHandler) {
    motePeripheral.setDidUpdateCharacteristicCompleteCallback { (updatedCharacteristic) -> Void in
            let decoded = updatedCharacteristic.getDecoded() 
            print("Done reading and decoding Read 
                characteristic: \(updatedCharacteristic) with 
                Value: \ . (decoded)")

            completionHandler(true)
        }
    }
Lauren
  • 29
  • 5
  • 1
    Why would you want this? You could simply continue your work in the completion handler. – Cristik Feb 06 '19 at 21:29
  • If you really need this, some solutions can be found here: https://stackoverflow.com/questions/42484281/waiting-until-the-task-finishes – Cristik Feb 06 '19 at 21:29
  • 1
    You need to embrace the asynchronous nature of IO operations. Trying to make them synchronous isn't the right approach. – Paulw11 Feb 06 '19 at 21:32
  • @Cristik Eh I'm most likely over complicating it, but I am trying to write a simple framework that allows you to create a Test object composed of various BLE command objects (read/write/notify) which run in a loop when you start the test. I want to know when the test is actually done, so I need to know when each command is done..so I didn't want to consider the read command done until I received the updated value. – Lauren Feb 06 '19 at 21:40
  • 1
    You can use test expectations to wait until the data is received. Or, if you really need synchronous, check the SO post I linked. – Cristik Feb 06 '19 at 21:42
  • Ok thanks @Cristik – Lauren Feb 06 '19 at 21:44
  • When you say “test”, are you talking about unit tests? In that case, you’d use `XCTestExpectation`, not semaphores or groups as contemplated in that linked answer. – Rob Feb 07 '19 at 00:27
  • And if you pursue that dispatch group or semaphore solution, make sure that the thread that you’re waiting on is not used by the completion handler, or else you’ll deadlock. And don’t block main thread, or else watchdog process may kill your app. – Rob Feb 07 '19 at 00:28
  • @Rob, sorry it's kind of confusing no not a unit test..trying to make a ble "Test" app to help integration test some hardware – Lauren Feb 07 '19 at 15:09
  • In that case, you should just abandon this idea of having the function wait until the closure is called asynchronously later. It’s just the wrong way to do this. They made that method asynchronous for a reason, namely all sorts of problems can manifest themselves if you block the thread. Instead, embrace the asynchronous behavior. For example, rather than having a loop that repeatedly calls “run”, instead just have the completion handler call “run” itself, and you’re done. – Rob Feb 07 '19 at 16:08
  • @Rob Thanks for the advice! That helps, I was thinking about this from the wrong way – Lauren Feb 07 '19 at 16:45

0 Answers0