Swift serial DispatchQueue not executing serially

160 Views Asked by At

Hey in my code the function "sendRequest" is called multiple times which sends a request to a server. The server processes all requests one after the other and sends back a multiple responses. Now I want to process these responses in the function "handleResponse", but in a serial queue. So handleResponse will only be called once at the same time. But now I have the case that my logs look like this:

[15.05.2023 11:28:00.711] DEBUG NetworkDataHandler:52 - start handleResponse
[15.05.2023 11:28:00.710] DEBUG NetworkDataHandler:52 - start handleResponse
[15.05.2023 11:28:00.713] DEBUG NetworkDataHandler:52 - start handleResponse
[15.05.2023 11:28:00.713] DEBUG NetworkDataHandler:52 - start handleResponse
[15.05.2023 11:28:00.714] DEBUG NetworkDataHandler:52 - start handleResponse
...

handleResponse is thus called several times, although it has not yet been completed. How can I get the callback to work serially?

Simplified code:

func sendRequest() { // is called several times
    applicationNetworkManager().sendQuery(
        ...
        successHandler: { response in
 
            let workItem = DispatchWorkItem {
                self!.handleResponse(response: response)
            }
            let serialQueue = DispatchQueue(label: "serialQueue", qos: .background)
            serialQueue.async(execute: workItem)
        }
    )
}

func handleResponse(response: Response) {
    Logger.d("start handleResponse")

    // do some stuff and write response data to local storage

    DispatchQueue.main.async {
        // update ui
    }

    Logger.d("end handleResponse")
}


1

There are 1 best solutions below

6
Paulw11 On BEST ANSWER

In sendRequest you are creating a new dispatch queue each time you call let serialQueue = DispatchQueue(label: "serialQueue", qos: .background). Even though all of the queue instances have the same label, they are different queues.

You don't show how you create the class that contains sendRequest, so I am not sure if there is one instance of this class or if you have multiple instances.

It is also best to avoid the .background QoS; This QoS level can be starved for CPU in a low power situation. .utility is a good choice.

Assuming you have one instance you would use something like:

class NetworkHandler {

    let myQueue: DispatchQueue

    init() {
        self.myQueue = DispatchQueue(label:"serialQueue", qos: .utility)
    }
}

You can then use this queue in your handler.