GCD DispatchWorkItem Swift Cancelling A Task In Dispatch

In IOS there are two famous API’s to handle multi-threading i.e:

  • GCD (Grand Central Dispatch)
  • Operation Queue

In this article I am going to talk about GCD Dispatch and a term called Debounce which ensures that a method is not executed too frequently.

Let’s began with DispatchWorkItem. How does DispatchWorkItem works ??

Suppose you tell your child to bring water he goes to the kitchen bring water for you and tells you here is you water. In this case your child is the DispatchWorkItem and you are the main-queue who is being notified that the task is completed.

In a same way if your child is half way to the kitchen and you suddenly tell him ok leave it I don’t need it anymore he will stop himself from this task and notify you with a reply ‘ok’. In this case the task is cancelled. Again you are acting as main-queue and you child is acting as DispatchWorkItem.

Let’s see some practical examples:

struct DispatchStruct {func workItemExample(){var myNumber : Int = 15let workItem = DispatchWorkItem {// Code you need to execute in workitemmyNumber += 5}// Create a Queue where you want to exexcute the workitemlet queue = DispatchQueue.global(qos: .utility)queue.async(execute: workItem)//Once completedworkItem.notify(queue: .main) {print("workitem is completed", myNumber)}}}let obj = DispatchStruct()obj.workItemExample()

Above we created a struct inside that struct we declared a function :

struct DispatchStruct {func workItemExample(){

The function contains a variable of type int and a workItem

struct DispatchStruct {func workItemExample(){var myNumber : Int = 15let workItem = DispatchWorkItem {// Code you need to execute in workitemmyNumber += 5}

Inside the workItem that is the DispatchWorkItem we are incrementing the number by 5.

Since we’ve created our workItem now we need a queue to execute it lets create the queue.

// Create a Queue where you want to exexcute the workitemlet queue = DispatchQueue.global(qos: .utility)queue.async(execute: workItem)

We created a queue and executing our workItem. Its like you asked your child for the water he went to the kitchen but still you are not notified i.e the task is not completed to get you the main-thread notified about task completion we need to call the following function

//Once completedworkItem.notify(queue: .main) {print("workitem is completed", myNumber)}

now once the task is completed lets see what’s get printed on the console.

Yes! we are notified about the completion and also the number incremented.

But wait how to cancel it???? What if you don’t want the water!!! How will you ask your child not to bring the water 🤯🤯🤯.

Dont worry stick with me 😎😎😎 will guide you about it in the next line.

workItem.cancel()

Here is the two magical words to cancel the workItem. Easy isn’t ?? lets look at practical example.

struct DispatchStruct {func cancelWorkItemExample(){var workItem : DispatchWorkItem!workItem = DispatchWorkItem {for i in 0...100 {guard let newWorkItem = workItem, !newWorkItem.isCancelled else {print("WorkItem is cancelled")break}print(i)sleep(1)}}//Once completedworkItem.notify(queue: .main) {print("workitem is completed")}let queue = DispatchQueue.global(qos: .utility)queue.async(execute: workItem)//cancel the workitem after delayqueue.asyncAfter(deadline: .now() + .seconds(3)) {//Do required task asfter 3 secondsworkItem.cancel()}}}let obj = DispatchStruct()obj.cancelWorkItemExample()

Above is the code where I’ve created a func which will run a for loop with a time delay of 1 second after each round of the loop.

struct DispatchStruct {func cancelWorkItemExample(){

We’ve created a struct and a function inside that struct called cancelWorkItemExample() .

Next we created a variable of type DispatchWorkItem .

struct DispatchStruct {func cancelWorkItemExample(){var workItem : DispatchWorkItem!

Now initialize the workItem as DispatchWorkItem & execute the code you want to in the workItem.

struct DispatchStruct {func cancelWorkItemExample(){var workItem : DispatchWorkItem!workItem = DispatchWorkItem {for i in 0...100 {guard let newWorkItem = workItem, !newWorkItem.isCancelled else {print("WorkItem is cancelled")break}print(i)sleep(1)}}

Inside the DispatchWorkItem I am running a for loop in range between 0 to 100 and in every single loop I am checking if the workItem is cancelled or not if it is cancelled then we print “WorkItem is cancelled” & break the for loop else we keep on printing the i.

if workItem.isCancelled {// code if cancelled}

This is how you can check if the workitem is cancelled.

After the above code we have some repetitive steps that I’ve explained already.

//Once completedworkItem.notify(queue: .main) {print("workitem is completed")}let queue = DispatchQueue.global(qos: .utility)queue.async(execute: workItem)

What’s new here is this

//cancel the workitem after delayqueue.asyncAfter(deadline: .now() + .seconds(3)) {//Do required task asfter 3 secondsworkItem.cancel()}

we created a queue and now we are asking the for a delay of 3 seconds and call the function cancel() of workItem. which means the workItem will cancel after 3 seconds.

Let’s execute our code:

As you can see it only printed 0,1,2 and then it was cancelled once cancelled mean you told your child not to bring water his reply ‘ok’ here is the ‘workItem is completed’ the workItem still notify the main-thread that I am completed and no more using the resources.

Debounce:

I’ve already explained what is debounce but let me explain you theratically how to achieve this using DispatchWorkItem.

Let’s say you have search bar where on every keyword enter to the search bar you are displaying the result to user. That means on every stroke of a key the view is being reloaded and API is being called.

If we calls the API on every stroke we are draining the user’s mobile data.

So how can we fix it by using DispatchWorkItem

To fix the above problem when ever user going to type something on search-bar we will wait for 1 second to check if there is a new input from user if not then we will call the api.

Let’s say you a function searchItem() who gets called inside searchBarDelegate i.e when ever user types something in the searchBar . Now under searchBar delegate instead of calling searchItem() directly you will create a workItem and inside that workItem you will call searchItem().

Then create a queue and execute the workItem with the required delay.

But what if the user types something before the 1 second our queue will look something like this:

This is how our queue looks if user doesn’t type anything before the async time.

This is how it will look if user typed something before the execution time.

To handle this kind of issues you need to write something like this:

I wrote the above code just for your understanding in playground. Everytime the searchBar delegate gets called on line 15 if there is any workItem in the queue it will get cancelled. A new workItem will be created and on line 25 the new workItem will be added in queue now our code only has 1 workItem in queue until the delegate is called again.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Umair Ishrat Khan

Umair Ishrat Khan

An energetic and motivated individual IOS developer/ Data Science Practitioner. Apart from computer science Martial arts interests me the most.