Skip to content

Instantly share code, notes, and snippets.

@leopic
Created July 18, 2020 21:47
Show Gist options
  • Save leopic/d5354b046dba5cd7503d1658efc71aa8 to your computer and use it in GitHub Desktop.
Save leopic/d5354b046dba5cd7503d1658efc71aa8 to your computer and use it in GitHub Desktop.
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
// Taken from https://www.avanderlee.com/swift/asynchronous-operations/
class AsyncOperation: Operation {
private let lockQueue = DispatchQueue(label: "com.leopicado.asyncop", attributes: .concurrent)
override var isAsynchronous: Bool {
return true
}
private var _isExecuting: Bool = false
override private(set) var isExecuting: Bool {
get {
return lockQueue.sync { () -> Bool in
return _isExecuting
}
}
set {
willChangeValue(forKey: "isExecuting")
lockQueue.sync(flags: [.barrier]) {
_isExecuting = newValue
}
didChangeValue(forKey: "isExecuting")
}
}
private var _isFinished: Bool = false
override private(set) var isFinished: Bool {
get {
return lockQueue.sync { () -> Bool in
return _isFinished
}
}
set {
willChangeValue(forKey: "isFinished")
lockQueue.sync(flags: [.barrier]) {
_isFinished = newValue
}
didChangeValue(forKey: "isFinished")
}
}
override func start() {
guard !isCancelled else {
finish()
return
}
isFinished = false
isExecuting = true
main()
}
override func main() {
fatalError("Subclasses must implement `main` without overriding super.")
}
func finish() {
isExecuting = false
isFinished = true
}
}
enum MyError: Error {
case error
}
class Op1: AsyncOperation {
var output: Result<[String], MyError>! {
didSet {
print("op1 done")
finish()
}
}
override func main() {
DispatchQueue.global().asyncAfter(deadline: .now() + 3.0) {
self.output = Bool.random() ? .success(["1", "2"]) : .failure(.error)
}
}
}
class Op2: AsyncOperation {
var input: Result<[String], MyError>!
var output: Result<Bool, MyError>! {
didSet {
print("op2 done")
finish()
}
}
override func main() {
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
guard case .success = self.input else {
self.output = .failure(.error)
return
}
self.output = .success(true)
}
}
}
let opQueue = OperationQueue()
let op1 = Op1()
let op2 = Op2()
// Connect both operations using a mediator
let adapterOperation = BlockOperation {
op2.input = op1.output
}
// The adapter needs op1 to finish before setting up op2
adapterOperation.addDependency(op1)
// Op2 needs to be setup by the adapter
op2.addDependency(adapterOperation)
// Setup the queue
opQueue.addOperations([op1, op2, adapterOperation], waitUntilFinished: true)
print("op2.output?", try? op2.output.get())
PlaygroundPage.current.finishExecution()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment