TL;DR see below for a code example of the problem.
I have two functions async update() that updates some data on the server and waits for the response. I have the option to provide a callback or make the function async and wait for the response. The second function fetch() lives in a different class (actually a different @StateObject, i.e. ObservableObject class - I'm using SwiftUI).
The function fetch() is triggered from a view, but it should "wait" for the update() function to have finished (if there is one running) before executing.
I want to keep the two class instances -where these functions are part of- separate. Thus I cannot explicitly use callback or alike. After searching and discovering DispatchQueue, DispatchGroup, Task, OperationQueue and concepts of synchronous/asynchronous, serial/concurrent I am quite confused on what to use since many of them seem to be very similar tools.
- DispatchQueue (& Task) - At first I thought DispatchQueue was the way to go since the docs seem to describe exactly what I need: "... Dispatch queues execute tasks either serially or concurrently. ..." The DispatchQueue.main is serial so I thought simply executing both function calls like this would wait for the - update()to finish if there was one started anywhere in my app and- fetch()would run afterwards. But apparently:- DispatchQueue.main.async { self.example = await self.update() }- That throws an error: - Cannot pass function of type '() async -> ()' to parameter expecting synchronous function type - That already seems weird to me because of its name - DispatchQueue.main.- async. I can avoid this by wrapping the call in a- Task {}but then I suppose it gets run on a different thread, as it did not finish the- Taskbefore running the- fetch()function. Thus it was no longer serial:- DispatchQueue.main.async { Task { print("1") try! await Task.sleep(nanoseconds: 10_000_000_000) print("2") } Task { print("3") } } // prints 1,3, ..., 2- Using - DispatchQueue.main.synccould seem more of what I need according to this, but I get the error:- No exact matches in call to instance method 'sync' - Is there a way to use - DispatchQueueto accomplish my goal? (I also tried creating my own queue with the same result as using the global main queue. I would want to use an asynchronous function and block any other function on this queue until after finishing execution)
- DispatchGroup - Next I tried using - DispatchGroupas shown here but was already thrown off by having to pass around this- DispatchGroup()instance to reference in both classes. Is it the only way to accomplish my goal? Could I avoid passing one single object to both classes at initalisation?
- OperationQueue - After reading further I stumbled across - OperationQueuehere. Again, this seems to solve my issue, but I once again have to pass this- OperationQueueobject to both classes.
Can someone explain the distinguishing differences of these approaches in relation to my problem? Which one is the "proper" way to do it? I would assume not having to pass around some objects is easier, but then how can I use a global DispatchQueue to serially execute some async functions?
Here a MRE, after clicking "start long task" and right away fetch the console should read: "1","2","these would be some results"
import SwiftUI
class Example1ManagerState: ObservableObject {
    func update() async {
        print("1")
        try! await Task.sleep(nanoseconds: 10_000_000_000)
        print("2")
    }
}
class Example2ManagerState: ObservableObject {
    func fetch() {
        print("these would be some results")
    }
}
struct ContentView2: View {
    
    @StateObject var ex1 = Example1ManagerState()
    @StateObject var ex2 = Example2ManagerState()
    
    var body: some View {
        Button {
            Task {
                await ex1.update()
            }
        } label: {
            Text("start long task")
        }
        
        Button {
            ex2.fetch()
        } label: {
            Text("fetch")
        }
    }
}
 
     
     
    