iOS Concurrency with GCD & Operations

Sep 12 2023 · Swift 5.8, macOS 13, iOS 16, Xcode 14.3

Part 3: Operations & OperationQueues

19. Challenge: Download Images in OperationQueue

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 18. Asynchronous Operations Next episode: 20. Dependencies

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 19. Challenge: Download Images in OperationQueue

Challenge: Download Images in OperationQueue

The playground in the starter folder has part of the ImageLoadOperation and the same image url components you used in the dispatch group challenge in Part 1. The first part of your challenge is to fill in the operation’s main function, to download an image from the input url. The second part of your challenge is to use instances of this operation in an OperationQueue to download all the images.

Hint 1: This challenge is similar to the dispatch group challenge in Part 1. Hint 2: Use the operation’s completion block.

Pause this video while you complete this challenge, then resume playing the video to see my solution.

AsyncOperation is in Sources/AsyncOperation.swift:

open class AsyncOperation: Operation {
  ...
}

It’s the same as you created in the previous exercise, but with access levels, because now it’s in a separate file. Here’s part of the ImageLoadOperation with input url and output image. Your first challenge is to fill in main:

class ImageLoadOperation: AsyncOperation {
  private let url: URL
  var image: UIImage?

  init(url: URL) {
    self.url = url
    super.init()
  }
  
  // TODO: Call dataTask with url and save image
  override func main() {
    
  }
}

First, call dataTask and capture self:

URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
  guard let self else { return }

}.resume()

It’s a good idea to call resume before you forget. In the dispatch group wrapper, you deferred group.leave to make sure it happens no matter how you leave the handler. Here, you’ll defer setting state to finished:

defer { self.state = .finished }

Now check there’s no error and there is data:

guard error == nil, let data else { return }

And finally, save data as a UIImage in the image property:

image = UIImage(data: data)

Now, to use this new operation: Here are the same image url components you used in the dispatch group challenge, the empty array to store the downloaded images, and an operation queue:

let base = "https://cdn.kodeco.com/books/con/image-from-rawpixel-id-"
let ids = [466881, 466910, 466925, 466931, 466978, 467028, 467032, 467042, 467052]
var images: [UIImage] = []
let queue = OperationQueue()

And here’s the loop for creating the operations:

for id in ids {
  guard let url = URL(string: "\(base)\(id)-jpeg.jpg") else { continue }

}

The playground supplies the code to construct the url. Create the operation and its completion block, then add it to the queue:

let op = ImageLoadOperation(url: url)
op.completionBlock = {
  if let image = op.image { images.append(image) }
}
queue.addOperation(op)

If the operation’s image isn’t nil, you add it to the array. Run the playground, then quick-look the image:

3.188688039779663 (or ~1.284000039100647 or 2.58...)
w 600 h 516

That’s how long it took to download all 9 images. Remember when you did the dispatch group challenge in Part 1, the images continued loading even when you scrolled away. Dispatch groups don’t have a way to start and cancel these requests, but operations do. In the final exercise of this course, you’ll cancel an Image’s operations when it scrolls off the screen.