Marking a method or variable with @MainActor doesn't guarantee that it will be called on the main thread - but that's what people are probably expecting, and this can lead to errors.
It's pretty easy to get caught out with a call to a @MainActor method on a background thread - particularly if you're using ObjectiveC or frameworks/libraries with asynchronous callbacks (like CoreBluetooth).
If XCode automatically gave purple runtime warnings whenever this happened - then people would at least be prodded to investigate.
Obviously this would only be for debug builds, and perhaps disable-able with a build flag.
I have no idea how much overhead it would add to check the thread on every call.
In your examples, you show that Obj-C method dispatch can be used to invoke a @MainActor function from another thread. I think that's likely unintentional - in other words, a bug. Have you filed a Github issue or Apple feedback report about it?
It's not a diagnostics issue; it's a memory safety issue.
As for solutions, maybe it's possible for the Obj-C versions of these functions to be secretly be some kind of wrapper which checks the thread?
Is the -Xfrontend -enable-actor-data-race-checks command-line option what you're looking for? This enables runtime warnings (or fatal errors if you set the appropriate env variable) for using an actor-confined thing from outside that actor. I assume it will become the default in Swift 6.
FWIW, the app works correctly for me when running iOS target on macOS. Testing on macOS 12.6
Without filing the bug there's always an excuse: "you didn't file the bug â we didn't know about it â so it is not fixed". If you do file the bug at least the ball is on the apple side.
It appears that there is a documented guaranteeâyou quote itâwhich is not met: that would be a bug. We should meet guarantees that we document or we shouldnât document them.
First-party documentation that says âThe compiler guaranteesâŚâ is very strong language, and if the guarantee isnât upheld, there is little wiggle room here: something is wrong and itâs not you.
Oh wow, I totally missed that one. Yeah, that seems like a very straightforward bug that definitely shouldnât compile when concurrency checking is enabled. Looks like weâre skipping actor isolation checks when we convert a key path literal to its corresponding function.
It appears that there is a documented guaranteeâyou quote itâwhich is not met: that would be a bug. We should meet guarantees that we document or we shouldnât document them.
First-party documentation that says âThe compiler guaranteesâŚâ is very strong language, and if the guarantee isnât upheld, there is little wiggle room here: something is wrong and itâs not you.
Is that documented?
I haven't found it in official documentation.
(I haven't found any official documentation of what @MainActor does...)
'The Swift Programming Language' only mentions @MainActor once in passing whilst discussing Sendable.
According to swift.org, that's the 'comprehensive guide and formal reference'
I thought it was your blog which quotes this line from Apple:
By adding the new @MainActor annotation to Photos, the compiler will guarantee that the properties and methods on Photos are only ever accessed from the main actor.
The quote is an example of the kind of description I see in less formal contexts.
In this case, it was a quote from an Apple rep in a WWDC talk.
I wouldn't call that a documented guarantee, and it was only intended as an example of what people describe/expect.
There is of course lots like that in tutorials/forums/etc - as well as in many other Apple talks.
For example in 'Eliminate data races using Swift Concurrency', there is a stronger quote:
Isolation to the main actor is expressed with the MainActor attribute.
This attribute can be applied to a function or closure to indicate that the code must run on the main actor.
Then, we say that this code is isolated to the main actor.
The Swift compiler will guarantee that main-actor-isolated code will only be executed on the main thread, using the same mechanism that ensures mutually exclusive access to other actors.
but again - I don't take WWDC talks as 'documentation'
The documentation youâre looking for here probably falls out of the proposal for actors (SE-0306) and global actors (SE-0316). Iâm not sure youâll find quite such a pithy quote , but SE-0316 does say this:
Note that the data in this view controller, as well as the method that performs the update of this data, is isolated to the @MainActor . That ensures that UI updates for this view controller only occur on the main thread, and any attempts to do otherwise will result in a compiler error.
And the proposals are broadly clear that, at least within Swift, actor isolation isnât really âoptionalââit should be that anything isolated to an actor (global or otherwise) cannot be synchronously accessed from outside that isolation domain.
One of the tests for the actor data race warning is also something that I'd expect to be a compile error as it's very straightforwardly doing something which violates actor isolation. The other one which uses an unsafeBitCast is reasonable though.
I also agree that the other @MainActor loopholes @ConfusedVorlon identified are important to either close or document very explicitly. @ConfusedVorlon Thank you for your articles about these problems!