1

Based on the order of the output in the xcode's console, it seems to suggest that default assignment happens before the execution of init(). However, the assignment in init() does not take effect. Here is the code:

TestStateVar(a_stateVar: 5)

...

struct TestStateVar: View {
    @State private var stateVar = -1
    
    init(a_stateVar: Int) {
        print("init-1: self.stateVar = \(self.stateVar)|a_stateVar = \(a_stateVar)")
        self.stateVar = a_stateVar
        print("init-2: self.stateVar = \(self.stateVar)")
    }
    
    var body: some View {
        Text("Test")
            .onAppear() {
                print("onAppear: self.stateVar = \(self.stateVar)")
            }
    }
}

Here is the output from the xcode's console:

init-1: self.stateVar = -1|a_stateVar = 5
init-2: self.stateVar = -1

onAppear: self.stateVar = -1

Is it possible, i.e., using some sort of linting, to configure xcode to detect this bug?

1
  • These similar posts, post-1, post-2, may be helpful. Commented Jan 31, 2024 at 6:02

2 Answers 2

2

try using this way to initialise a @State var, such as:

init(a_stateVar: Int) {
     print("init-1: self.stateVar = \(self.stateVar) | a_stateVar = \(a_stateVar)")
     _stateVar = State(initialValue: a_stateVar) // <--- here
     print("init-2: self.stateVar = \(self.stateVar)")
 }
Sign up to request clarification or add additional context in comments.

4 Comments

Do you know if it's possible for xcode to detect this incorrect pattern of using "self.<stateVar>", to initialize a state var, and treat it as an error? I'd think it's common for new users of SwiftUI to make this mistake.
don't know how to set Xcode to do that. I doubt it can be done
I just found out that the proper way/syntax (_stateVar = State(initialValue: a_stateVar) is not needed if I remove the default value, i.e., I can use the common way of "self.stateVar = ...", and that works. Do you know if there is documentation that explains the different options of initializing/setting @State var?
In IMHO, init() is not a good place to initialise a @State var, because a View is called/initialised at any time SwiftUI needs it. You are supposed to have the parent view declare @State private var stateVar: Int = -1, the single source of truth. In your parent view change stateVar = 5 somewhere, then call TestStateVar(stateVar: stateVar) where you declare let stateVar: Int. If you need to change stateVar in TestStateVar, use @Binding var stateVar: Int and call TestStateVar(stateVar: $stateVar). Note, updated my answer, removing proper.
1

After assigning the value of a_stateVar to self.stateVar in init, you see that it's still -1, because the @State property is not being used in the method, and the assignment is made to the local struct property, not the @State property.

You have 2 ways to fix this:

  • The answer provided by @workingdog support Ukraine
  • Remove the default of stateVar, and put it on the init argument

the second-way solution:

struct TestStateVar: View {
    @State private var stateVar: Int

    init(a_stateVar: Int = -1) {
        stateVar = a_stateVar
        print("init-2: self.stateVar = \(self.stateVar)")
    }

    var body: some View {
        Text("Test")
            .onAppear() {
                print("onAppear: self.stateVar = \(self.stateVar)")
            }
    }
}

2 Comments

What do you mean by "... the assignment is made to the local struct property, not the @State property"? Are you saying the SwiftUI framework internally creates some "hidden property" for "stateVar"? If so, is that "hidden property" instance accessible to me? I'd like to verify that behavior if possible.
Exactly! SwiftUI uses the @State property wrapper to allow us to modify values inside a struct, which would normally not be allowed because structs are value types. When you annotate the name property with the @State property wrapper, SwiftUI will synthesize 2 additional properties: $stateVar and _stateVar.

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.