Sometimes I need to make a series of network calls where each one depends on the prior ones, so they must be done in series. Typically, network calls take a completion handler as an argument. A series of calls can be done via nested completion handlers, but that gets difficult to read.
As an alternative, I've been dispatching the process to a global queue and using a Grand Central Dispatch DispatchSemaphore
to stall until the network query returns. But it doesn't seem very elegant. Here is some example code:
let networkSemaphore = DispatchSemaphore(value: 0)
var body: some View {
Text("Hello, world!")
.padding()
.onAppear { doChainOfEvents() }
}
func networkFetch(item: String, callback: @escaping (Int) -> Void) {
print("Loading \(item)...")
DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(2)) {
let numLoaded = Int.random(in: 1...100)
callback(numLoaded)
networkSemaphore.signal()
}
networkSemaphore.wait()
}
func doChainOfEvents() {
DispatchQueue(label: "com.test.queue.serial").async {
networkFetch(item: "accounts") { num in
print("\(num) accounts loaded.")
}
networkFetch(item: "history") { num in
print("\(num) messages loaded.")
}
networkFetch(item: "requests") { num in
print("\(num) requests loaded")
}
print("Network requests complete. ✓")
}
print("Control flow continues while network calls operate.")
}
and the printed result of doChainOfEvents()
:
Control flow continues while network calls operate. Loading accounts... // (2 second delay) 79 accounts loaded. Loading history... // (2 second delay) 87 messages loaded. Loading requests... // (2 second delay) 54 requests loaded Network requests complete. ✓
Can you think of more elegant way to achieve this? It seems to me there ought to be one within using Grand Central Dispatch. I could use a DispatchGroup
in place of the semaphore, but I don't think it would buy me anything, and a group with one item at a time seems silly.