0

My scenario is like this. I need to send a Network Request, in response i will become a list of Image URLs. I would then need to send multiple network requests concurrently to get all of these Images and once i have all the Images downloaded , I need to populate a tableView.

To fulfil these requirements i am trying to understand the relationship between DispatchQueue and DispatchGroup in Playground and so far this is my code.

var concurrentQueue = DispatchQueue(label: "test",attributes: .concurrent)
var myGroup = DispatchGroup()
var mySecondGroup = DispatchGroup()

myGroup.notify(queue: DispatchQueue.main) {
    print("Initial Json request with List of URLs completed")
    mySecondGroup.enter()
    concurrentQueue.async {
        for i in 10...15 {
            print(i)
            if(i==15){
                mySecondGroup.leave()
            }
        }
    }
}

mySecondGroup.notify(queue: DispatchQueue.main) {
    print("All Images download complete")
}

myGroup.enter()
concurrentQueue.async {
    for i in 0...5 {
        print(i)
        if(i==5){
            myGroup.leave()
        }
    }
    
}

The problem is i am getting the result

0
1
2
3
4
5
Initial Json request with List of URLs completed
10
11
All Images download complete
12
13
14
15

But what i want is something like this

0
1
2
3
4
5
Initial Json request with List of URLs completed
10
11
12
13
14
15
All Images download complete

I am not really understanding what i am doing wrong here and any help would be greatly appreciated.

dev_ios999
  • 339
  • 3
  • 9
  • https://pastebin.com/U7k32tKs should work. – Larme May 18 '21 at 17:09
  • how does it work ? – dev_ios999 May 18 '21 at 17:12
  • You need to match an "enter" with each "leave", that's how the group knows when the set of tasks has completed. – john elemans May 18 '21 at 17:16
  • But i think i have done that. We start myGroup.enter() and then myGroup.leave() which in turn triggers the notify method of myGroup where i do mySecondGroup.enter() and then after process mySecondGroup.leave(). So i think i have matched each "enter" with "leave" – dev_ios999 May 18 '21 at 17:19
  • But, you called `mySecondGroup.enter()` too late, it should have called already its `notify()`. Also, put notify in the end (at least after a `enter()` by custom), else it might also be called too soon. – Larme May 18 '21 at 17:20
  • I don't understand. Why would mySecondGroup call its notify method at all before mysecondGroup.leave is called. – dev_ios999 May 18 '21 at 17:23
  • That's not what is happening. You have more leaves than enters, so the second notify occurs before you want it to. – john elemans May 18 '21 at 17:37
  • How ? As far as i understand i have myGroup.enter() and then inside the if condition myGroup.leave() that should trigger the myGroup.notify(). Inside this method i have another mySecondGroup.enter() and then in inside the if condition i have another mySecondGroup.leave(). 1 Enter and 1 Leave respectively on each group. Maybe i am understanding it all wrong ? – dev_ios999 May 18 '21 at 17:41
  • Are your prints consistent? – Don May 18 '21 at 18:11
  • `DispatchGroup` is pointless for a single asynchronous task because you can proceed directly in the completion handler. – vadian May 18 '21 at 18:37
  • @Don Yes, I am getting the same results everytime – dev_ios999 May 18 '21 at 18:39
  • @vadian But i have 2 asynschronous task. First is to download the json and get all the image urls and second is to download all the images concurrently. – dev_ios999 May 18 '21 at 18:40
  • With *a single asynchronous task* I mean one call without a loop. – vadian May 18 '21 at 18:43
  • The reason I asked if your prints are consistent is because you can't rely on their output from different queues like that. Wrap _all_ your prints in a `DispatchQueue.main.async` block and you'll get your expected output every time. – Don May 18 '21 at 19:14

1 Answers1

3

DispatchQueue is the medium where the tasks are scheduled on.

DispatchGroup works like a counter. Calling enter increments the counter (usually before calling the asynchronous task) and leave decrements the counter (usually inside the completion handler). When the counter reaches zero the group notifies.

Your scenario needs something like this

let concurrentQueue = DispatchQueue(label: "test",attributes: .concurrent)
let group = DispatchGroup()
concurrentQueue.async {
    asynchronousAPI.call() { urls in
        for url in urls {
            group.enter()
            asynchronousImageLoad(url) { data in
               // process data
               group.leave()
            }
        }
    }
}
group.notify(queue: .main) {
    print("done")
}
vadian
  • 274,689
  • 30
  • 353
  • 361