Warning in nonisolated init setting constants

Hi!

After updating to Xcode 15.3, I get the warning:

:warning: Main actor-isolated property 'foo' can not be mutated from a non-isolated context; this is an error in Swift 6

in code constructs like this:

@MainActor
final class SomeViewModel: ObservableObject {
  @Published private(set) var state = ...
  
  private let foo: String
  private let bar: (String) -> Void

  nonisolated init(
    foo: String,
    bar: @escaping (String) -> Void
  ) {
    self.foo = foo // <-- No warning here
    self.bar = bar // <-- Warning here
  }

  ...
}

Questions:

  1. Why shouldn't I be able to set private constants in the initialiser of a class like this from a non-isolated context? (The instance is not created, so how can it be "mutating" the private let property?

  2. Why do I only get the warning for bar and not also for foo?

  3. Is adding nonisolated to the private let property with the warning an acceptable fix for this?

1 Like

String conforms to Sendable, so it is safe for foo to cross isolation boundary. Closure you pass to init is not. Adding @Sendable attribute will fix the warning.

Because bar property by not being sendable cannot safely cross isolation boundary (from main-actor isolation to nonisolated context) at the time of assign. Probably, compiler message here not that descriptive; the case not in "mutability", but non-Sendable closure is crossing isolation boundary.

3 Likes
  1. Is adding nonisolated to the private let property with the warning an acceptable fix for this?

Hmm, the warning is still there after adding nonisolated like this:

  nonisolated private let bar: (String) -> Void

Depends on use case, e.g. you would need to access this nonisolated property from nonisolated context. Making it @Sendable will make it usable within the class easily, and probably this is better strategy to resolve the warning.

1 Like

There's an explanation for why this happens at Assigning to a `let` instance variable in nonisolated init - #4 by hborla

tldr; the compiler should not let you write nonisolated on stored properties of Sendable/actor-isolated types when the stored property type is not Sendable. This is fixed in the Swift 6 language mode.

2 Likes

Using Xcode 16 beta and enable Swift 6, I can remove the warning with below code

@MainActor
final class SomeViewModel: ObservableObject {
    @Published private(set) var state = ...

    private let foo: String
    nonisolated private let bar: @Sendable (String) -> Void

    nonisolated init(foo: String,
         bar: @Sendable @escaping (String) -> Void) {
        self.foo = foo 
        self.bar = bar
    }
}
1 Like