2

I've got the following code, which seems very simple.

import SwiftUI

struct Tester : View
{
    @State var blah : String = "blah"

    func setBlah(_ val : String) {
        blah = val
    }
    var body: some View {
        Text("text")
    }
}

var test = Tester()
test.setBlah("blee")
print(test.blah)

I would normally expect to see the final print statement display "blee", but it is "blah" -- the variable never changed. If I pass blah into another view via a binding, I am able to change the value of blah there. Any insight here would be appreciated -- including rtfm, if you can point me to the right manual -- I have looked, and haven't found an answer.

Edit: after reading @jnpdx 's answer, I have a slightly different version, but it still doesn't work -- I'm not worried about this specific code working, but trying to understand the magic that @jnpdx refers to, in terms of how @State works, and why a binding passed to another view is able to modify the original @State variable, while I am unable to within the struct itself. I am guessing there is some part of SwiftUI that needs to be instantiated that the @State property's communicate with in order to store the variables outside of the struct, as the apple documentation says. New version follows:

import Foundation
import SwiftUI

struct Tester : View
{
    @State var blah : String

    func setBlah(_ val : String) {
        $blah.wrappedValue = val
    }

    var body: some View {
        Text("smoe text")
    }
}

var test = Tester(blah: "blah")
test.setBlah("blee") // expect to see blee printed, but get nil instead
print(test.blah)

Thanks :)

4
  • The "new version" is not any different. You cannot reach into a View's State from the parent and modify it. Plus, your View isn't in a hierarchy and so who knows if SwiftUI is doing anything to manage the state (my guest is that it isn't). Any attempt you have to reach in and modify @State like this will be unreliable at best and flat out not work at worst. Commented Jul 13, 2022 at 0:43
  • right yeh -- the new version wasn't an attempt to make it work, but to point out the kind of code that, in another view, would update the value. I believe the problem is that it isn't in a hierarchy, and so SwiftUI isn't doing anything. I was hoping to find out what part of SwiftUI actually is taking over management of the value. I'm not trying to make this code work exactly -- I'm trying to understand what SwiftUI is doing that makes this kind of thing work when it's part of a larger project. I wish it was open source, so I could look at the code. Thanks much for your help :) Commented Jul 13, 2022 at 22:03
  • 1
    Yeah, without the source, it is certainly opaque. I’d recommended watching Demystifying SwiftUI from WWDC 2021, though — that will help. Commented Jul 13, 2022 at 22:23
  • Thanks much, I'll definitely check that out :) Commented Jul 16, 2022 at 8:01

1 Answer 1

2

@State in SwiftUI doesn't work like simple mutating functions on a struct -- it's more like a separate layer of state that gets stored alongside the view hierarchy.

Let's look at what this would have to look like if it were not SwiftUI/@State:

struct Tester
{
    var blah : String = "blah"

    mutating func setBlah(_ val : String) {
        blah = val
    }
}

var test = Tester()
test.setBlah("blee") // prints correctly
print(test.blah)

Note that above, setBlah has to be marked mutating because it mutates the struct. Whereas in your example, the compiler doesn't require it, because the struct itself is not actually mutating -- the @State property wrapper is doing some behind-the-scenes magic.

Check out the documentation on State: https://developer.apple.com/documentation/swiftui/state

In particular:

Don’t initialize a state property of a view at the point in the view hierarchy where you instantiate the view, because this can conflict with the storage management that SwiftUI provides. To avoid this, always declare state as private, and place it in the highest view in the view hierarchy that needs access to the value. Then share the state with any child views that also need access, either directly for read-only access, or as a binding for read-write access.

By marking @State as private, you can prevent things like outside entities trying to manipulate it directly. However, in your example, you've circumvented this a bit by making a setter function that would avoid the private issue even if it were included. So, really, setBlah should be marked private as well.

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.