@stackotter and I have come across an example where hooking into MainActor dynamically would be useful.
When using a GUI framework like GTK, the main thread is never serviced and @MainActor
functions never run. Instead, you'd need a separate actor (let's call it @UIActor
) to coordinate scheduling work with GTK so that it always runs with exclusive isolation with UI updates. This is annoying, but possible; I have successfully implemented such a UIActor
.
However, we can't mark SwiftCrossUI's View
protocol as @UIActor
, because that causes problems in other backends -- particularly in UIKitBackend's UIViewRepresentable
, since UIView
is @MainActor
. There's no way (as far as I'm aware) to tell the compiler that @MainActor
and @UIActor
are the same thing when using the UIKitBackend, and even if we used assumeIsolated
in UIViewRepresentable
's implementation, any user who wants to use any other UIKit API (e.g. calling a method on UIApplication.shared
inside a button callback) will run into the same issue.
Additionally, I believe this is technically possible on macOS, but I'm not sure why one would do this:
import SwiftCrossUI
import AppKitBackend
import GtkBackend
struct MyGtkApp: App {
let backend = GtkBackend()
var body: some Scene { ... }
}
struct MyAppKitApp: App {
let backend = AppKitBackend()
var body: some Scene { ... }
}
public func main() {
if someDynamicRuntimeCondition() {
MyGtkApp.main()
} else {
MyAppKitApp.main()
}
}
In which case whether @MainActor
works out-of-the-box or not is decided dynamically at runtime.