I maintain a framework called Adobels/UIViewKit, which provides a DSL for configuring UIViews and their Auto Layout constraints using @resultBuilder, allowing views to be written in code instead of relying on Storyboards.
When migrating to Swift 6 with strict concurrency enabled, I noticed that inside my DSL closures, UIView properties (like backgroundColor) are shown in autocomplete as if they were async. For example:
Is the async marker in autocomplete caused by something in my DSL design (e.g. how the @resultBuilder closure is typed), or is it a current limitation/bug in the compiler or IDE when combining @resultBuilder with @MainActor classes?
If it's the latter, is this already a known issue?
P.S. I tried adding @MainActor in all possible places to be more explicit, but that has not solved the issue. Autocompletion was tested in Xcode 16.4 and 26.0 Beta 4.
I see this with Apple’s APIs all the time, it’s either an Xcode bug or it means something non obvious, like it’s isolated or something. I don’t think there’s anything for you to do.
It is clear that the @MainActor annotation causes this, there is no concurrency otherwise. No XCode either. Windows platform, Swift 6.1.3, Swift 6 language mode. Code suggestions are provided by sourcekit-lsp. Function xyzzy() was needed because the async marker does not appear in top level code.
That one kind of makes sense, as calling a @MainActor isolated function from a non isolated context would require an await. (It would be more useful if the autocomplete suggestion added the await for you rather than just indicating async. But I see it for any isolated value even when I'm in the same context. If you add @MainActor to xyzzy, does it still appear?
Oops, that was because I temporarily annotated xyzzy as @MainActor and forgot to remove the annotation. The async annotation in code suggestions is shown in the async version of xyzzy (when it is nonisolated) for await foo.bar().
Yes, now I see that this async marker in code suggestions makes sense.
TBH I never thought about the fact that I may need to await a synchronous function (or property for that matter). But it looks like I should. I have a real life project that does not use Swift Concurrency, but has some classes annotated as @MainActor isolated, because they are UI-related.
It turns out that merely annotating a class @MainActor effectively turns it into an actor?
Here is the spiral of doom:
I don't want or need to use concurrency, but I need to work with UI
I don't think in these terms (but maybe I should).
I think in terms of single-threaded application, but since my project is a framework, the context from which things will be called is up to end user.