# Reconsider inference of global actor based on property wrappers

**URL:** <https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821>\
**Category:** Discussion\
**Tags:** concurrency\
**Created:** [October 12, 2022, 8:07pm UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821 "2022-10-12T20:07:08Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![bjhomer](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bjhomer/32/313_2.png) [@bjhomer](https://forums.swift.org/u/bjhomer)\
**Post date:** [October 12, 2022, 8:07pm UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/1 "2022-10-12T20:07:08Z")

</div>

Currently, we can infer `@MainActor` on an entire type based on the presence of certain property wrappers within that type. **TL;DR: I'm making a case that that's a bad idea, and we should reconsider it if possible.**

When talking about global actor inference, [SE-0316 (Global Actors) states](https://github.com/apple/swift-evolution/blob/main/proposals/0316-global-actors.md#global-actor-inference):

> Declarations that are not explicitly annotated with either a global actor or `nonisolated` can infer global actor isolation from several different places:
> 
> [...]
> 
> - A struct or class containing a wrapped instance property with a global actor-qualified wrappedValue infers actor isolation from that property wrapper:
> 
> ```swift
> 
> ```

I have found that **this particular inference rule is surprising and nonobvious** to most users. In practical usage, this often comes up when using SwiftUI. I frequently deal with developers who have trouble understanding the Swift Concurrency model, because it's not obvious to them when actor isolation applies. When something is inferred, it is not visible to the user, and that makes it harder to understand.

```swift
@MainActor func mainActorStuff() {}

// @MainActor -- inferred from presence of @StateObject var
struct MyView: View {
    @StateObject private var model = Model()
    var body: some View {
        Text("Hello, \(model.name)").onAppear { updateUI() }
    }

    func updateUI() {
      mainActorStuff()
    }
}

```

The above view compiles just fine. But if we change `@StateObject` to `@State`…

```swift
func updateUI() {
    // error: Call to main actor-isolated global function
    // 'mainActorStuff()' in a synchronous nonisolated context
    mainActorStuff()
}

```

In my experience, it's not at all obvious to most developers that changing `@StateObject` to `@State` should cause some function that doesn't even touch that property to stop compiling. The same happens if we remove an unused `@StateObject` property entirely.

In fact, we can also get errors if we add a _new_ property:

```swift
@propertyWrapper
struct DBObject<Wrapped> {
  @DatabaseActor var wrappedValue: Wrapped
}

struct MyView: View {
  // 👇 This is new 
  @DBObject private var dbConnection = DBConnection()
  @StateObject private var model = Model()
  // ...
}

```

When we have _two_ property wrappers that have `wrappedValue`s isolated to two different actors, then the inference just silently stops, and you're back to having to declare it manually. The compiler doesn't tell you that it's doing this; it just silently infers (or doesn't infer) it. This feels a lot like Einstein's "spooky action at a distance", where changing one property declaration can cause another function to fail to compile, even if it doesn't use that property at all.

It's not clear to me why the inference based on property wrappers was proposed initially. I didn't find any discussion of it during the Global Actors review. I suspect it may have been to make it easier to interact with `@ObservedObject` when SwiftUI first rolled out. But I'm not sure it actually makes it significantly easier; it only saves us from writing a single annotation, and the loss of that annotation causes its own confusion, as shown above. (See also [this recent tweet](https://twitter.com/teilweise/status/1580105376913297409?s=61&t=hwuO4NDJK1aIxSntRwDuZw) and [this recent blog post](https://oleb.net/2022/swiftui-task-mainactor/))

I'd propose that **we should stop inferring actor isolation based on property wrappers, perhaps in Swift 6**. That would definitely break some code right now, but as we ramp up the enforcement of actor isolation in upcoming Swift releases, the breakage would likely increase. In practice, the source breakage could be limited if `SwiftUI.View` were annotated `@MainActor` directly. (I recognize that SwiftUI isn't in the purview of Swift Evolution, though. But it does give Apple a way to mitigate the breakage for their users.)

Is there anything I'm missing that justifies the current inference behavior?

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [October 12, 2022, 8:26pm UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/2 "2022-10-12T20:26:44Z")

</div>

Oh wow, I think when I read this initially I interpreted that as inferring actor-isolation _for the wrapped property_, which, of course it does. I agree it seems weird that this inferred isolation propagates 'up' to the type level... all the other isolation inference rules operate at the same 'level' (i.e., types infer isolation based on the isolation of other types/protocols, members infer isolation from other members) or I suppose in the 'downward' direction (since we infer actor isolation of members from the isolation of the containing type).

Also curious what the motivating use case for this was. Can't recall if it was discussed in review but the proposal as-written doesn't sufficiently motivate this surprising behavior, IMO.

---

<div class="post-metadata">

**Author:** ![AliSoftware](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/alisoftware/32/2764_2.png) [@AliSoftware](https://forums.swift.org/u/AliSoftware)\
**Post date:** [October 14, 2022, 4:06pm UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/3 "2022-10-14T16:06:00Z")

</div>

Yes please!

Those rules of bubbling the actor inference to the type based on its properties feels so random!

---

<div class="post-metadata">

**Author:** ![davedelong](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/davedelong/32/38708_2.png) [@davedelong](https://forums.swift.org/u/davedelong)\
**Post date:** [October 17, 2022, 2:14pm UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/4 "2022-10-17T14:14:51Z")

</div>

Yeah, I came across this recently and was completely caught off guard by this behavior. I eventually found [this post](https://www.hackingwithswift.com/quick-start/concurrency/understanding-how-global-actor-inference-works) from Paul Hudson explaining why it was happening and was quite mystified about the whole thing.

I would also love to see this reconsidered. This is spooky action at a distance.

---

<div class="post-metadata">

**Author:** ![moreindirection](https://avatars.discourse-cdn.com/v4/letter/m/858c86/32.png) [@moreindirection](https://forums.swift.org/u/moreindirection)\
**Post date:** [October 20, 2022, 11:01am UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/5 "2022-10-20T11:01:04Z")

</div>

Wow, that is surprising. I agree this behavior should have to be explicit.

---

<div class="post-metadata">

**Author:** ![anreitersimon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/anreitersimon/32/7269_2.png) [@anreitersimon](https://forums.swift.org/u/anreitersimon)\
**Post date:** [January 20, 2023, 7:24am UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/6 "2023-01-20T07:24:52Z")

</div>

I just encountered this and find it very unexpected, and its currently presenting a pretty big hurdle in adopting async/await.

i have some existing propertyWrappers that ensure that i am calling them on the main-thread.

simplified version here

```swift
struct OnMainThread<Value> {
    var _wrapped: Value
  
    public var wrappedValue: Value {
        set {
            dispatchPrecondition(condition: .onQueue(.main))
            _wrapped = newValue
        }
        get {
            dispatchPrecondition(condition: .onQueue(.main))
            _wrapped
        }
    }
}

```

and im often using initializers with default-expressions

```swift
class MyContainer {
     let contained = Contained() // error: Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
}

class Contained {
    @OnMainThread var int = 1
}

```

this pattern is quite common in our codebase

i now wanted to introduce the `@MainActor` on my propertyWrapper and it breaks in a LOT of places.

I find the behaviour very unintuitive, since i have not annotated the `Contained` class itself with `@MainActor`

Is there room to reconsider this?

---

<div class="post-metadata">

**Author:** ![anreitersimon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/anreitersimon/32/7269_2.png) [@anreitersimon](https://forums.swift.org/u/anreitersimon)\
**Post date:** [January 20, 2023, 7:51am UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/7 "2023-01-20T07:51:11Z")

</div>

Also is there a way to opt out of this behaviour other than adding a property isolated to a different actor?

i dont really like the idea of adding "dummy" properties with wrappers just to disable this behaviour.

---

<div class="post-metadata">

**Author:** ![KeithBauerANZ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/keithbaueranz/32/27897_2.png) [@KeithBauerANZ](https://forums.swift.org/u/KeithBauerANZ)\
**Post date:** [January 24, 2023, 3:59am UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/8 "2023-01-24T03:59:33Z")

</div>

I completely agree that this particular deduction is extremely unexpected. I believe it was added so that SwiftUI could remain source-compatible?

In general, I find all global actor inference to be problematic. It's basically impossible when looking at a declaration to know whether it's actor-isolated or not, without inspecting arbitrarily many influences (superclasses, protocols, property wrappers). And rules may differ between main implementations and extensions. I'd like to see Swift 6 consider removing global actor inference entirely (with fix-its suggesting adding an explicit global actor annotation when problems arise)

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [January 24, 2023, 4:02am UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/9 "2023-01-24T04:02:09Z")

</div>

Hi all, so that we don't lose track of this thread, perhaps one or more of you who's a proponent of making a source-breaking change in Swift 6 might be interested in referencing and briefly summarizing the discussion over in the thread about [Swift 6 language mode design priorities](https://forums.swift.org/t/design-priorities-for-the-swift-6-language-mode/62408)?

---

<div class="post-metadata">

**Author:** ![KeithBauerANZ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/keithbaueranz/32/27897_2.png) [@KeithBauerANZ](https://forums.swift.org/u/KeithBauerANZ)\
**Post date:** [January 24, 2023, 4:10am UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/10 "2023-01-24T04:10:37Z")

</div>

Thanks, I've added my 2¢ here: [Design Priorities for the Swift 6 Language Mode - #64 by KeithBauerANZ](https://forums.swift.org/t/design-priorities-for-the-swift-6-language-mode/62408/64)

---

<div class="post-metadata">

**Author:** ![ConfusedVorlon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/confusedvorlon/32/14854_2.png) [@ConfusedVorlon](https://forums.swift.org/u/ConfusedVorlon)\
**Post date:** [March 2, 2024, 4:07pm UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/11 "2024-03-02T16:07:10Z")

</div>

100%  
This kind of side effect from adding a variable is absurd.

Even if it was clearly documented, it's a ridiculous thing to do.

If the presence of @StateObject _requires_ main actor everywhere, then it should require the _user_ to set that at a class level (by giving an error when you try to add @StateObject without setting the annotation yourself at the class level)

Automatically 'inferring' it, applying it everywhere, and telling nobody is absurd.

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [March 2, 2024, 5:41pm UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/12 "2024-03-02T17:41:11Z")

</div>

Just for reference, this discussion let to a proposal which was reviewed and [accepted](https://forums.swift.org/t/accepted-with-modifications-se-0401-remove-actor-isolation-inference-caused-by-property-wrappers/66241) last year.

---

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [March 2, 2024, 8:47pm UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/13 "2024-03-02T20:47:53Z")

</div>

And this is why resurrecting year old posts is bad.

---

<div class="post-metadata">

**Author:** ![joshw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joshw/32/34626_2.png) [@joshw](https://forums.swift.org/u/joshw)\
**Post date:** [March 4, 2024, 5:25pm UTC](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/14 "2024-03-04T17:25:09Z")

</div>

Also encountered this issue recently since the latest Xcode RC (15.3) has stricter concurrency checking.

For those like myself who don't read all the proposals; while this has technically been "solved" it still requires some work in Swift 5 to disable this behavior.

tldr; if you'd like to disable `@MainActor` property wrappers like `@StateObject`, `@EnvironmentObject`, etc from make their enclosing type `@MainActor` you'll need to enable experimental feature `DisableOutwardActorInference `.

In an SPM package:

```swift
targets: [
    .target(
        name: ...,
        dependencies: ...,
        swiftSettings: [
            .enableUpcomingFeature("DisableOutwardActorInference"),
        ]
    )
]

```

In an Xcode project:

 ![Screenshot 2024-03-04 at 9.23.17 AM](https://global.discourse-cdn.com/swift/original/3X/2/f/2fa1c8637230d7c9b2369448bf403c08d8d79130.jpeg)
