Use @MainActor alongside existing DispatchQueue code

I am currently trying to adapt concurrency into my existing codebase, where I am for instance using DispatchQueue.main to dispatch back to the main queue.
As far as I've understood we now should be able to use @MainActor to do nearly the same but with less boilerplate. Nevertheless I am currently stuck with the following problem:

DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 5) { @MainActor [weak self] in
            self?.doStuffOnMain()
        }

When using the following code, the compiler complains that Converting function value of type '@MainActor () -> Void' to '@convention(block) () -> Void' loses global actor 'MainActor'. I assume thats because the DispatchQueue's block is not marked with @MainActor itself, but I thought with the code above the closure gets dispatched to main automatically. Did I get that wrong? Do I still have to use DispatchQueue.main?
In addition to that, if I try to run the above code, the doStuffOnMain() function isn't called on the main thread at all.

Hope someone can shed some light on that, thanks!

What was the reason to use background queue here at all to begin with?

Should have been simply:

DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in
    self?.doStuffOnMain()
}

The code snippet was just for outlining the issue. This could also be a network request which calls a completion handler on a background thread.

You can specify the queue where network request completion handler completes (via URLConfiguration). In most (all?) API's this queue specifying is possible one way or another (typically you pass the queue parameter, which is optional (if not specified the system picks some queue).

Back to your question, as a practical workaround I'd do smth like:

    DispatchQueue.global().async {
            // do something in background
           Task {
                await doStuffOnMain()
           }
    }
    
    @MainActor func doStuffOnMain() { ... }

Or even good old DispatchQueue.main.async {...}

Actor and associated concurrency checks/errors are new but they are not yet "production ready" and are somewhat half-backed. A trivial example:

import Dispatch

class C {
    @MainActor func doStuffOnMain() {}
    
    func foo() {
        DispatchQueue.main.async {
            self.doStuffOnMain() // ok
        }
    }

    func bar() {
        let queue = DispatchQueue.main
        queue.async {
            self.doStuffOnMain()
            // Error: error: call to main actor-isolated instance method 'doStuffOnMain()' in a synchronous nonisolated context
        }
    }

    func baz() {
      typealias DispatchQueue = MySomethingElse
      DispatchQueue.main.async { // this is not what it looks
        self.doStuffOnMain() // ok
      }
    }
}

struct MySomethingElse {
  static var main: DispatchQueue { DispatchQueue.global() }
}

Here foo and bar are obviously doing the same thing, yet one compiles and another doesn't. And 'baz' compiles fine, but shouldn't. The checks are shallow and not robust yet.

Yes thanks, I know, but as I said, its just for sketching the problem :slight_smile:

Thanks, wrapping the call in a Task does work, but in the context of a class in turn self has to be Sendable so that I can use it inside the Task..

And coming back to one of my initial questions, isn't it intended to pass @MainActor into a closure so the closure is called on main?