I'd like to propose a Xcode feature which helps user to determine where an func runs or which actor a property belong to. This is a pre-pitch for adding compiler support for it.
Note: since I don't know compiler internals, this pre-pitch is more like a software requirement document, not a design document.
How it will work in Xcode
The feature will work in Xcode in a similar way as how we view a variable's type using Alt-click
. I'll use the following code as an example.
1 import SwiftUI
2 struct ContentView: View {
3 @State var data = "Hello"
4 var body: some View {
5 VStack(spacing: 16) {
6 Button("Test") {
7 Task {
8 data = await getData()
9 }
10 }.buttonStyle(.bordered)
11 Text("Data: \(data)")
12 }
13 }
14 }
15 private func getData() async -> String {
16 return "World"
17 }
When user clicks on data
in line 3, Xcode should show "non-isolated".
When user clicks on somewhere inside the Task closure (line 7-9), Xcode should show "main-actor".
When user clicks on getData()
in line 8, however, Xcode should show something like ... I don't have a good name for it, maybe "non-isolated"?
If user clicks in a custom actor's code (the example doesn't have it), Xcode should show the actor's name. In Task.detached
case, it would be something like "anonymous actor - <id>" (the id is to differentiate different anonymous actors).
Concurrency information available at compile time
I'm aware some information are available only at runtime. One example is which exact thread in the cooperative thread pool getData()
will run in. The feature doesn't intend to provide details like that, because a) it can't, and b) in most case what user'd like to know is if a func runs in main actor, a custome actor, or in a non-isolated way. My understanding is those information are available at compile time.
Why error message isn't enough
I notice that error message contains helpful information to debug concurrency issue. But it has a few limitations:
First, it's a "trial and error" approach.
Second and more importantly, it doesn't help user understanding working code. There are situations where the code works but user misunderstands how it actually works. Take the above code as an example, I misundertood how it worked at first. Given the fact that a) I read from the doc that Task
inherits the current actor context, and b) the compiler didn't complain about line 8, I thought that data
was protected by main actor. But that's not true, and it's hard to figure it out without experiments.
I personally think this will be a useful feature and from my understanding it's feasible. What do you people think? Comments are welcome.