iOS Concurrency with GCD & Operations

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

Part 3: Operations & OperationQueues

18. Asynchronous 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: 17. Challenge: TiltShiftOperations in OperationQueue Next episode: 19. Challenge: Download Images in OperationQueue

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've reached locked video content where the transcript will be shown as obfuscated text.

Often, you’ll want to execute a chain of asynchronous tasks, like download an image, then resize it, then apply a filter to the resized image. If you just call the next task in the completion handler of the preceding task, you’ll get something like this pyramid of doom, with several levels of nesting.

extension AsyncOperation {
  enum State: String {
    case ready, executing, finished
  }
}
var state = State.ready
override var isReady: Bool {
  super.isReady && state == .ready
}

override var isExecuting: Bool {
  state == .executing
}

override var isFinished: Bool {
  state == .finished
}

override var isAsynchronous: Bool {
  true
}
override var isExecuting: Bool {
  state == .executing
}

override var isFinished: Bool {
  state == .finished
}
override var isAsynchronous: Bool {
  true
}
// TODO: Override start method
override func start() {

}

// TODO: Override cancel method
override func cancel() {

}
override func start() {
  if isCancelled {
    state = .finished
    return
  }
  main()
  state = .executing
}
override func cancel() {
  super.cancel()
  state = .finished
}
private let stateQueue = DispatchQueue(label: "AsyncOperationState",
                                       attributes: .concurrent)
private var stateValue: State = .ready
var state: State {
  get {
    stateQueue.sync { return stateValue }
  }
  set {

  }
}
fileprivate var keyPath: String {
  "is\(rawValue.capitalized)"
}
set {
  let oldValue = state
  willChangeValue(forKey: newValue.keyPath)
  willChangeValue(forKey: state.keyPath)
  stateQueue.sync(flags: .barrier) {
    stateValue = newValue
  }
  didChangeValue(forKey: oldValue.keyPath)
  didChangeValue(forKey: state.keyPath)
}
class AsyncSumOperation: AsyncOperation {
  let lhs: Int
  let rhs: Int
  var result: Int?
  
  init(lhs: Int, rhs: Int) {
    self.lhs = lhs
    self.rhs = rhs
    super.init()
  }
  
  override func main() {
    DispatchQueue.global().async {
      sleep(2)
      self.result = self.lhs + self.rhs
      self.state = .finished   // Add only this line
    }
  }
}
let queue = OperationQueue()
let pairs = [(2, 3), (5, 3), (1, 7), (12, 34), (99, 99)]
pairs.forEach { pair in
  let op = AsyncSumOperation(lhs: pair.0, rhs: pair.1)
  op.completionBlock = {
    guard let result = op.result else { return }
    print("\(pair.0) + \(pair.1) = \(result)")
  }
}
queue.addOperation(op)
queue.waitUntilAllOperationsAreFinished()
99 + 99 = 198
5 + 3 = 8
1 + 7 = 8
2 + 3 = 5
12 + 34 = 46