1

I have a VM that is implemented as follows:

LoginViewModel

class LoginViewModel: ObservableObject {
    
    var username: String = ""
    var password: String = ""
}

In my ContentView, I use the VM as shown below:

 @StateObject private var loginVM = LoginViewModel()
    
    var body: some View {
      
        
        NavigationView {
            
            Form {
                TextField("User name", text: $loginVM.username)
                TextField("Password", text: $loginVM.password)

Every time I type something in the TextField it shows the following message in the output window:

Binding<String> action tried to update multiple times per frame.
Binding<String> action tried to update multiple times per frame.
Binding<String> action tried to update multiple times per frame.

It is a message and not an error.

If I decorate my username and password properties with @Published then the message goes away but the body is rendered each time I type in the TextField.

Any ideas what is going on and whether I should use @Published or not. I don't think I will gain anything from putting the @Published attribute since this is a one-way binding and I don't want to display anything on the view once the username changes.

Mary Doe
  • 1,073
  • 1
  • 6
  • 22

2 Answers2

3

If I decorate my username and password properties with @Published then the message goes away

This is the correct solution. You need to use @Published on those properties because that is how SwiftUI gets notified when the properties change.

the body is rendered each time I type in the TextField

That is fine. Your body method is not expensive to compute.

I don't think I will gain anything from putting the @Published attribute since this is a one-way binding

You cannot be sure SwiftUI will work correctly (now or in future releases) if you don't use @Published. SwiftUI expects to be notified when the value of a Binding changes, even when a built-in SwiftUI component like TextField causes the change.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
0

For the simple case - the state is kept in the same view or in a ModelSupport class, consists of strings or other primitive types, and there's only one of each, @Published will work fine.

I got this error with a model class containing an array of structs and using a List, and every time you type inside a TextField inside a list (or every time you select an item in a list), the view gets refreshed, and the error gets triggered.

I am thus using a DelayedTextField:

struct DelayedTextField: View {
 
        var title: String = ""
        @Binding var text: String
        @State private var tempText: String = ""

        var body: some View {
            TextField(title, text: $tempText, onEditingChanged: { editing in
                if !editing {
                    $text.wrappedValue = tempText
                }
            })
            .onAppear {
                tempText = text
            }
        }
    }

and the binding update error is no more.

green_knight
  • 1,319
  • 14
  • 26