To achieve that with dispatch groups alone you would need three
dispatch groups which are entered and left accordingly:
let abGroup = DispatchGroup()
let bcGroup = DispatchGroup()
let acGroup = DispatchGroup()
abGroup.enter()
abGroup.enter()
bcGroup.enter()
bcGroup.enter()
acGroup.enter()
acGroup.enter()
// When a is updated:
abGroup.leave()
acGroup.leave()
// When b is updated:
abGroup.leave()
bcGroup.leave()
// When c is updated:
acGroup.leave()
bcGroup.leave()
Then you can wait for the completion of each group independently
abGroup.notify(queue: .main) {
// Do something with a and b
}
bcGroup.notify(queue: .main) {
// Do something with b and c
}
acGroup.notify(queue: .main) {
// Do something with a and c
}
However, this does not scale well with more tasks and dependencies.
The better approach is to add Operation
s to an
OperationQueue
, that allows to add arbitrary dependencies:
let queue = OperationQueue()
let updateA = BlockOperation {
// ...
}
queue.addOperation(updateA)
let updateB = BlockOperation {
// ...
}
queue.addOperation(updateB)
let updateC = BlockOperation {
// ...
}
queue.addOperation(updateC)
let doSomethingWithAandB = BlockOperation {
// ...
}
doSomethingWithAandB.addDependency(updateA)
doSomethingWithAandB.addDependency(updateB)
queue.addOperation(doSomethingWithAandB)
let doSomethingWithBandC = BlockOperation {
// ...
}
doSomethingWithBandC.addDependency(updateB)
doSomethingWithBandC.addDependency(updateC)
queue.addOperation(doSomethingWithBandC)
let doSomethingWithAandC = BlockOperation {
// ...
}
doSomethingWithAandC.addDependency(updateA)
doSomethingWithAandC.addDependency(updateC)
queue.addOperation(doSomethingWithAandC)
For asynchronous request with completion handlers you can use a
(local) dispatch group inside each block operation to wait for the
completion.
Here is a self-contained example:
import Foundation
var a: String?
var b: String?
var c: String?
let queue = OperationQueue()
let updateA = BlockOperation {
let group = DispatchGroup()
group.enter()
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0, execute: {
a = "A"
group.leave()
})
group.wait()
print("updateA done")
}
queue.addOperation(updateA)
let updateB = BlockOperation {
let group = DispatchGroup()
group.enter()
DispatchQueue.global().asyncAfter(deadline: .now() + 2.0, execute: {
b = "B"
group.leave()
})
group.wait()
print("updateB done")
}
queue.addOperation(updateB)
let updateC = BlockOperation {
let group = DispatchGroup()
group.enter()
DispatchQueue.global().asyncAfter(deadline: .now() + 3.0, execute: {
c = "C"
group.leave()
})
group.wait()
print("updateC done")
}
queue.addOperation(updateC)
let doSomethingWithAandB = BlockOperation {
print("a=", a!, "b=", b!)
}
doSomethingWithAandB.addDependency(updateA)
doSomethingWithAandB.addDependency(updateB)
queue.addOperation(doSomethingWithAandB)
let doSomethingWithAandC = BlockOperation {
print("a=", a!, "c=", c!)
}
doSomethingWithAandC.addDependency(updateA)
doSomethingWithAandC.addDependency(updateC)
queue.addOperation(doSomethingWithAandC)
let doSomethingWithBandC = BlockOperation {
print("b=", b!, "c=", c!)
}
doSomethingWithBandC.addDependency(updateB)
doSomethingWithBandC.addDependency(updateC)
queue.addOperation(doSomethingWithBandC)
queue.waitUntilAllOperationsAreFinished()
Output:
updateA done
updateB done
a= A b= B
updateC done
a= A c= C
b= B c= C