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.

1

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.