简体   繁体   中英

DispatchGroup.wait not waiting

I'm having an issue understanding or using Dispatchgroup. I've read a lot about them however most examples / documentation are very vague or doesn't resemble what I want to do, however every time I mention my problem everyone says "USE DISPATCH GROUPS!".

Here's what I want to do(NOTE: SEQUENTIAL ORDER IS CRUCIAL):

  • Send Bluetooth Write characteristic.
  • device receives value, and spits something in response
  • Read Bluetooth response (via a read characteristic)
  • Send a new write characteristic (a different command)
  • device receives NEW command, spits NEW data response

Repeat twice (3 commands total, 3 different responses total).


My code:

func tPodInitialSetUp()
    {
        print ("* * * * * BEGIN SET-UP * * * * *")
        let setupDispatchGroup = DispatchGroup()

        setupDispatchGroup.enter()
        self.writeValue(command: Data(CommandModeCmd)) //231: Put t-Pod in command mode, burst mode is OFF returns OK
        setupDispatchGroup.leave()

        setupDispatchGroup.wait()

        setupDispatchGroup.enter()
        deviceConnected?.readValue(for: deviceConnectedCh1n2Char!)
        print("Sent command 231: returned: \(receivedString1)")
        if receivedString1.lowercased() == "ok"
        {
            print("t-Pod burst mode is OFF")
        }
        setupDispatchGroup.leave()

        setupDispatchGroup.wait()

        setupDispatchGroup.enter()
        self.writeValue(command: Data(loadProbeCalCmd)) //202: load calibration constants of probe, returns ok or 0
        setupDispatchGroup.leave()

        setupDispatchGroup.wait()

        setupDispatchGroup.enter()
        deviceConnected?.readValue(for: deviceConnectedCh1n2Char!)
        print("Sent command 202: returned: \(receivedString1)")
        if receivedString1.lowercased() == "ok"
        {
            print("Probe Constants loaded")
        }
        if receivedString1 == "0"
        {
            print("No probe connected")
        }
        setupDispatchGroup.leave()

        setupDispatchGroup.wait()

        setupDispatchGroup.enter()
        self.writeValue(command: Data(probeSNCmd)) //205: load probe serial number
        setupDispatchGroup.leave()

        setupDispatchGroup.wait()

        setupDispatchGroup.enter()
        deviceConnected?.readValue(for: deviceConnectedCh1n2Char!)
        print("Sent command 205: returned: \(receivedString1)")
        if (receivedString1.count == 6)
        {
            print("received Probe SN: \(receivedString1)")
            probeSN = receivedString1
        }
        setupDispatchGroup.leave()

        setupDispatchGroup.notify(queue: .main)
        {
            tPodSN = String(describing: connectedDeviceName!.dropFirst(7))
            print ("* * * SET-UP COMPLETE * * *")
            self.writeValue(command: Data(resetCmd)) //200: resets t-Pod
            self.writeValue(command: Data(beaconOffCmd)) //211: turns beacon off (temperature output)
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5)
        {
            self.dataDisplaySubView.isHidden = false
            print ("Adding observers!")

            NotificationCenter.default.addObserver(self, selector: #selector(self.updateIncomingData), name: NSNotification.Name(rawValue: DATA_PARSED), object: nil) //Run every time you receive data from BLE

            NotificationCenter.default.addObserver(self, selector: #selector(self.calculateTNU), name: NSNotification.Name(rawValue: TOGGLESWITCH_TOGGLED), object: nil) //Run in case the toggle switches change and data needs to be re-calculated

            NotificationCenter.default.addObserver(self, selector: #selector(self.parseReceivedData), name: NSNotification.Name(rawValue: DEVICE_FINISHED_SENT_DATA), object: nil) //Run every time you receive the notification that the whole data has been sent
        }
    }

This calls the bluetooth write command which has the following code and confirmation:

func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
        guard error == nil else {
            print("Error writing descriptor: " + (error?.localizedDescription)!)
            return
        }
        print("Descriptor Value sent")
    }

Now, Here's my output:

* * * * * BEGIN SET-UP * * * * *
***** WRITING *****
Wrote: 1 bytes
***** WRITING *****
Wrote: 1 bytes
Sent command 231: returned: **T-Pod-9Ch**
***** WRITING *****
Wrote: 1 bytes
Sent command 202: returned: **T-Pod-9Ch**
***** WRITING *****
Wrote: 1 bytes
Sent command 205: returned: **T-Pod-9Ch**
* * * SET-UP COMPLETE * * *
***** WRITING *****
Wrote: 1 bytes
***** WRITING *****
Wrote: 1 bytes
Characteristic Value sent
Adding observers!
Characteristic Value sent
Characteristic Value sent
Characteristic Value sent
Characteristic Value sent
Clearing TNU Array

Now, as you can see "Characteristic Value Sent" is the confirmation the bluetooth function gives when it sends the value, however this output is created AFTER it finished running the entire code, so basically it put the commands in some pipeline, forgot about them did everything else and THEN sent the commands and therefore the response I'm reading are all nonsense! As you can see all received strings are T-Pod-9Ch (which is just its normal burst output), the expected responses I should get from the commands are OK, OK and a 6 digit number (in that order).
Please help, I've read so many times about how dispatch groups are supposed to work but I just can't get them to do what I want.

If I got your question right, you need to wait for an answer before sending new command.

But your writes doesn't have a completion block, that's why in your case using dispatchGroup makes no sense.

The code below is a common example to use dispatch groups

func someMethod(completionHandler: @escaping ()-> Void) {


 //we need to get or set some data in separated queue
    DispatchQueue.global(qos: .background).async {

    let group = DispatchGroup()

    //let's say we have an array of urls and we need to load images and put them to an array
    for url in someUrlArray {
          group.enter()
          SomeLoaderClass.load(url) { image in
                //add received image

                //leave the group
               group.leave() 
           }
    }

    //now we need to wait until all images will be downloaded 
    group.wait()

    //then we can finish and call the completion in the main queue
    DispatchQueue.main.async {
         completionHandler()
    }
   }
}

In your situation you may have several options:

First, if you know that if you send one command and receive an answer exactly for that command, you can call methods in order below:

  1. Call one method to Send command 1

  2. Call another method after an answer for command 1 will be received

  3. Call yet another method to Send command 2

  4. And one more method after getting an answer for command 2 ...

n. Finish setup

Like if I need to register a user, I need to send defined credentials first, get token from the server, then run something after it.

So you will have to add an additional method for each command and you will call them according the order

If you can't recognize for which command you're going to get an answer and you sure that you've send only one command and waiting only one answer, then you can use dispatch group in the way described below:

typealias Callback = ()->Void
class SomeManagerClass {
       var callback: Callback?

       func initiateSetup(){
              DispatchQueue.global(qos: .background).async { [weak self] in
                    let group = DispatchGroup()
                   //now we can send commands
                   group.enter()
                   self?.sendCommand(SomeDataForTheFirstCommand) {
                          //when the first answer will be received, it will init the callback, so you can leave the group now
                         group.leave()
                    }
                   //sending the second command
                    group.enter()
                   self?.sendCommand(SomeDataForTheSecondCommand) {
                          //waiting for the second answer will be received
                         group.leave()
                    }

                    //.... more commands sending same way
                    group.wait()
                    //now all commands was send and you got an answer for each

                    //finishing setup 
                         DispatchQueue.main.async{
                        self?.finishSetup()
                   }
              }
       }

      func sendCommand(_ command: Data, callback: Callback?){
             self.writeValue(command: command)
             self.callback = callback 
      }

      func answerReceived(){
             //this is just an example method that is called when you get an answer for any command
            //now we can callback 
            self.callback?()
      }

      func finishSetup(){

           //do something 
      }
}

Let me know if you need more details

Example of using Dispatch group

private func performTask1() -> String {
    print("Task1")
    var result: String?
    let background = DispatchQueue.global(qos: .background)

    let dispatchGroup = DispatchGroup()

    dispatchGroup.enter()
    background.asyncAfter(deadline: .now() + 3) {
        result = "Task1 is performed"
         dispatchGroup.leave()
    }
   dispatchGroup.wait()
    return result!
}

Try this out to understand concept of DispactGroup!

Try! this

Required DispatchQueue.global(qos: .background) for leave()

func dispatchGroup() -> String {

   var message = ""

   let group = DispatchGroup()

   group.enter()

   DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 5) {
        message = "Hiii Friends"
        group.leave()
   }

   group.wait()

   return message
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM