iOS Concurrency with GCD & Operations

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

Part 3: Operations & OperationQueues

15. Operations

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: 14. Challenge: Make Number Class Thread-Safe Next episode: 16. OperationQueues

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.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Operations are built on top of GCD, adding features like dependencies on other operations, the ability to cancel the running operation, and an object-oriented model to support more complex requirements.

BlockOperation

In the starter playground, here’s a variable to hold the result of adding two numbers:

var result: Int?
let sumOperation = BlockOperation(   // pause to see initializers
let sumOperation = BlockOperation {

}
result = 2 + 3
sumOperation.start()
result
<NSBlockOperation 0x7fc623406b30 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>
5
sleep(2)  // below result = 2 + 3
duration {
  sumOperation.start()  // existing
}
<NSBlockOperation 0x7fc623406b30 isFinished=NO isReady=YES isCancelled=NO isExecuting=NO>
5
0
2.005437970161438
()
5
let multiPrinter = BlockOperation()
multiPrinter.addExecutionBlock {  print("Hello"); sleep(2) }
multiPrinter.addExecutionBlock {  print("my"); sleep(2) }
multiPrinter.addExecutionBlock {  print("name"); sleep(2) }
multiPrinter.addExecutionBlock {  print("is"); sleep(2) }
multiPrinter.addExecutionBlock {  print("Audrey"); sleep(2) }
duration {
  multiPrinter.start()
}
2.00261402130127
is
name
Hello
my
Audrey
// right after let multiPrinter = BlockOperation()
multiPrinter.completionBlock = {
  print("Finished multiPrinting!")
}
name
is
my
Hello
Audrey
Finished multiPrinting!

Subclassing Operation

In Subclassing Operation, click the run button on the inputImage line, then tap the Show result button to see the input image.

let inputImage = UIImage(named: "dark_road_small.jpg")
w 500 h 574 (in side pane)

Create TiltShiftOperation

Double-click on the playground name to go back there. Here’s a TiltShiftOperation class — a subclass of Operation — with a CIContext property — you’ll need this to convert the filter’s output to a CGImage:

class TiltShiftOperation: Operation {
  private static let context = CIContext()
...
}
private let inputImage: UIImage?
var outputImage: UIImage?
init(image: UIImage?) {
  inputImage = image
  super.init()
}
guard let inputImage,
      let filter = TiltShiftFilter(image: inputImage),
      let output = filter.outputImage else {
  print("Failed to generate tilt shift image")
  return
}
private static let context = CIContext()  // scroll up to top of class
let fromRect = CGRect(origin: .zero, size: inputImage.size)
guard let cgImage =
        TiltShiftOperation.context.createCGImage(
          output,
          from: fromRect)
else {
  print("No image generated")
  return
}
outputImage = UIImage(cgImage: cgImage)

Run TiltShiftOperation

So now, you’ve encapsulated all the functionality the operation should do. To use your new operation subclass, instantiate one with your inputImage:

let tsOp = TiltShiftOperation(image: inputImage)
duration {
  tsOp.start()
}
tsOp.outputImage
0.3255159854888916
...
w 500 h 574