2

Combined use @mainActor and propertyWrapper is not safe? The code will execute directly in the background thread. It is a bug?

Here is the demo:

struct DetailView: View {
    
    @MainActor
    @Environment(\.dismiss)
    private var dismiss
    
    var body: some View {
        Text("Hello")
            .task {
                await asyncWork()
            }
    }

    private func asyncWork() async {
        Thread.sleep(forTimeInterval: 1)

        // Crash. Because of not in main Thread.
        await dismiss()
    }

}
Chihi Lim
  • 21
  • 1
  • As an aside, you should avoid `Thread.sleep` within Swift concurrency. You should avoid violating the contract of forward progress. (See [preserving forward progress](https://developer.apple.com/videos/play/wwdc2021/10254/?time=1611) in _Swift concurrency: Behind the scenes._ It’s focusing on separate issue, but this runtime contract is discussed many times in this video.) Use `Task.sleep`, which introduces a suspension point rather than something that prevents forward progress. – Rob Mar 26 '22 at 20:10
  • I recall seeing a bug report regarding main actor and property wrappers. I’m digging around for it and haven’t found it yet… – Rob Mar 26 '22 at 20:15

1 Answers1

0

I found the answer. Swift concurrency will get dismiss in main queue. Then back to background execute the dismiss block.

It look like


@MainActor var dismiss: ()->(Void) {
  // get in main thread
  { ... }
}

func asyncWork() async {
  Thread.sleep(forTimeInterval: 1) // background thread
  let dismiss = self.dismiss // main thread
  dismiss() // background thread. 
}


Chihi Lim
  • 21
  • 1