.disabled with string vs. string in a class

I'm still pretty new to swift and swiftui but this seems pretty basic.


Two TextField/Button pairs - the goal is to disable the button when the corresponding text field is empty. Only difference between the sets is that one uses a String while the other uses a String within a class. I've tried it with and without the ObserveableObject and @Published declarations.


If I type in the first text field, the button lights up as expected. In the second, nothing. Huh?


class User: ObservableObject {

@Published var name: String


init() {

name = ""

}

}


struct ContentView: View {

@State var user = User()

@State var username = ""


var body: some View {

VStack {

TextField("New Name", text: $username)

Button(action: {print(self.username)}) {Text("String")}.disabled(self.username.isEmpty)

TextField("New Name", text: $user.name)

Button(action: {print(self.user.name)}) {Text("User Class")}.disabled(self.user.name.isEmpty)

}

}

}

Replies

I believe the cause is the use of an @State variable wrapping a class. The value hasn't changed (it's an address, and that address hasn't changed), so SwiftUI isn't redrawing the view. You'll notice that while the second button doesn't become enabled when you change the second text field, if you type into the first field while there is text in the second then both buttons will become enabled. This shows that it's SwiftUI not re-fetching the body of your view when the User object is modified.


When the value you're dealing with is a class conforming to ObservableObject, the appropriate attribute to use is @ObservedObject. Simply changing that one line makes SwiftUI look for published changes from that object, and everything works as you'd expect.


@ObservedObject var user = User()