'SwiftUI View not updating with binding to computed property on ObservableObject
I have a very simple SwiftUI view that only shows a TextField. The text field's text is bound to the string property of my viewModel that I instantiate as a @StateObject:
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
TextField("Placeholder", text: $viewModel.string)
.padding()
}
}
The one thing that's "out of the ordinary" is that my string property is not a @Published property, but a simple computed property. In its setter, it sets the displayedString property to a fixed string (for testing purposes) which is a @Published property:
class ViewModel: ObservableObject {
@Published var displayedString: String = ""
var string: String {
get { displayedString }
set { displayedString = "OVERRIDE" }
}
}
So when I type a string into the text field, I would expect the setter to trigger a view update (as it updates a @Published property) and then in the view, the text field should be updated with the displayedString. So in other words: No matter what I type, the text field should always show the string OVERRIDE.
But that's not the case!
It works for the very first letter I type, but then I can freely type anything into the text field.
For example, if I launch the app with only this view and type the string
123456789
this is what is displayed on the text field:
OVERRIDE23456789
Why is that?
And how can I get the text field to always be up-to-date to what's set on the viewModel? (I know, I can just hook the text field to a @Published property directly, but I'm doing this computed property stuff for a reason and want to understand why it doesn't work as expected.)
Additional Observation:
When I add a Text label above the TextField and just make it show the viewModel.string, it shows the correct (expected) string:
var body: some View {
Text(viewModel.string)
TextField("Placeholder", text: $viewModel.string)
.padding()
}
Solution 1:[1]
I’m not sure but I think a custom binding might work for you
In the ViewModel use
var string: Binding<String> { Binding(
get: { displayedString },
set: { displayedString = “OVERRIDE” } )}
Then in body use: viewModel.string instead of $viewModel.string in the TextField and use: viewModel.string.wrappedValue in the text view
Stuf
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Joseph Levy |

