0

If i don't have an initial value for a @State and set it in the init I get a Fatal Error: EXC_BAD_ACCESS, but only in Release Mode

public struct TestSetStateView: View {
  @State var item:Int
  public init(item: Int) {
    self.item = item
  }
  public var body: some View {
    Text("Hello: \(item)")
  }
}

If i add a default value

public struct TestSetStateView: View {
  @State var item:Int = 0
  public init(item: Int) {
    self.item = item
  }
  public var body: some View {
    Text("Hello: \(item)")
  }
}

and call it with TestSetStateView(item: 8) it shows "Hello: 0"

If I move that to onAppear

public struct TestSetStateView: View {
  @State var item:Int = 0
  let firstItem: Int
  public init(item: Int) {
    firstItem = item
  }
  public var body: some View {
    Text("Hello: \(item)")
      .onAppear(perform: {
        item = firstItem
      })
  }
}

and call it with TestSetStateView(item: 8) it shows "Hello: 8" which is great

or if i use the _ wrapper:

public struct TestSetStateView: View {
  @State var item:Int
  public init(item: Int) {
    _item = State(initialValue: item)
  }
  public var body: some View {
    Text("Hello: \(item)")
  }
}

and call it with TestSetStateView(item: 8) it shows "Hello: 8", which is also great!

I'm just not sure what's happening, why can i use item = firstItem in onAppear and not in init. I sort of understand that the _ uses the @State wrapper but why don't we have to use the in onAppear. Also not sure why the error in the first example only happens in release.

richy
  • 2,716
  • 1
  • 33
  • 42
  • Does this answer your question? [How could I initialize the @State variable in the init function in SwiftUI?](https://stackoverflow.com/questions/58758370/how-could-i-initialize-the-state-variable-in-the-init-function-in-swiftui) – Phil Dukhov Sep 29 '21 at 15:48

1 Answers1

1

This code:

public struct TestSetStateView: View {
  @State var item:Int
  public init(item: Int) {
    _item = State(initialValue: item)
  }
  public var body: some View {
    Text("Hello: \(item)")
  }
}

is correct. Until a State variable is initialized, you can't set the underlying value directly. Remember, State is a property wrapper. The type of @State var item is not Int, it is a State that happens to contain an Int. It could just as easily contain a Double or a String. Trying to set a State with a value of Int makes as much sense as setting a String with an Int. You have a type mismatch. Frankly, the compiler should have screamed that you can't do that, but instead you get a crash.

Once the State var is initialized, it knows how to handle changing its underlying wrapped value directly. You can also initialize it directly elsewhere. Even the code: _item refers to the wrapped value, and not the state itself. Why they chose different semantics in the State property wrapper initializer, I don't know. But they did, and excluded us from using the convenience init() of the @State declaration in our view inits.

Yrb
  • 8,103
  • 2
  • 14
  • 44