1

I have array of photos that needs to be downloaded, but download function can download only one photo at a time. I neeed to make sure that download function is completed before I call it for other photo. I create .initialyInactive DispatchQueue and queue is activated in completion block of download function. This is woking, and photos are downloaded, but my question is how to cancel download process? Can I somehow to remove not activated queues?
My code logic looks something like this..

 func downloadPhoto(photo: Photo, completion: (_ success: Bool) -> Void) {
    ... completion(true) ..
}
for photo in photos {
      let queue = DispatchQueue(label: "Download queue\(counter)", qos: .userInitiated, attributes: .initiallyInactive)
       self.inactiveQueues[counter] = queue

       queue.async {

           self.downloadPhoto(photo: photo) { (success) in
                  nextQueue?.activate()
                   if success {
                          ...
                   } else {
                          ...
                   }
           }
}

Ant other solution to my problem is great too. Thanks.

Ana
  • 21
  • 2
  • Are you constrained to perform one request at a time or it's only your function that allows you to fetch a single photo? Because there are mechanisms that help you start all download tasks at once and wait for all of them to finish. – Vlad Rusu Mar 26 '20 at 20:58
  • Also, with this setup, I'm afraid you cannot cancel inactive queues. Let's say that you keep the reference to the inactive queues and try to stop them. What happens if that just before you try to deactivate it, the completion block fires and starts it? – Vlad Rusu Mar 26 '20 at 21:01
  • I'm constrained to perform one request at a time. Basically, I need to call async function for certain number of times, but each call to function must be when previous is done. Also, I need to be able do cancel it. I'm pretty stuck here and out of ideas. Other setup that is going to work is great help, also! – Ana Mar 26 '20 at 22:37
  • Well, then you will need to call the download queue synchronously, not asynchronously. And you'll have run the `for` which starts the downloads in an async queue. Then all your calls will be triggered one after another. And that way you are able to keep a reference to the running download task, and even the download tasks that are not yet started (the inactive queues). – Vlad Rusu Mar 27 '20 at 11:33

1 Answers1

0

There are several options:

  1. Historically we’d use a custom, asynchronous, Operation subclass. See https://stackoverflow.com/a/38469350/1271826 (or see https://stackoverflow.com/a/48104095/1271826 for alternate AsynchronousOperation implementation). That gives us a cancelable operation and we can easily control the degree of concurrency.

    If you want to download them one at a time, you could set the queue’s maxConcurrentOperationCount to 1. Then you can just add your download operations to a single OperationQueue, and if you want to cancel them, you’d call queue.cancelAllOperations().

  2. If you are targeting iOS 13 and later, Combine makes this even easier. See https://stackoverflow.com/a/61195234/1271826 for example of downloading a series of images using Combine (both sequentially as well as with a controlled degree of concurrency).

All of this having been said, I’d encourage you to reconsider the choice to download the images sequentially. In my random test, increasing the max allowed concurrency to 6 resulted in downloads that were, overall, twice as fast as the serial behavior.

Community
  • 1
  • 1
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Thanks a lot for your answer. I solved my problem using Operation subclass, but your links are helpful anyway. Unfortunately, I can't download the images sequentially, because download function is third part library and function returns "system busy" error if I try sequentially behavior. – Ana Apr 14 '20 at 09:18
  • I assume when you say “I can’t download images sequentially...”, I assume you meant “concurrently”. OK, got it. So you ended up with the Operation subclass like I outlined in point 1, above? Cool. – Rob Apr 14 '20 at 13:44