How @MainActor preservation rule works?

Hi, I can confirm your SwiftUI example result. Also, enbaling strict concurrency check doesn't help in this case (no error or warning message on the silent ignorance of @MainActor). Based on the explanation here, I modified the code by removing async from all closure signatures. With that change, my experiment showed they all ran in main thread.

However, what I really want to say is that IMO your code are unnecessarily complex - @MainActor and async keywords show up in too many places, which is not only inefficient but also hard to reason about. Take the first part of the code an example, below is how I'd implement it (note that only run() is async and has @MainActor applied). Hope it helps.

struct RunActionView: View {
    let title: String
    let action: () -> Void

    init(title: String, action: @escaping () -> Void) {
        self.title = title
        self.action = action
    }

    var body: some View {
        Button(action: executeAction) {
            Text(title)
        }
    }

    private func executeAction() {
        print(title)
        Task {
            try await run(operation: action)
        }
    }
}

@MainActor
func run(operation: () throws -> Void) async throws {
    try operation()
}
1 Like