'Binding ViewModel and TextFields with SwiftUI

I'm looking for the best way to create a bind between textfields and ViewModel. At the moment I'm creating a @State for each textfield and I'm manually sending the value from textfield to the viewModel properties when needed. I'm pretty sure this is not the best way to go... Is there a way to bind the TextField with the ViewModel property?

This is my current code:

struct SigninView: View {
    @State var username:String = ""
    @State var password:String = ""

    var viewModel:SignInViewModel

    var body: some View {
        VStack(alignment: .leading, spacing: 15.0){

            DefaultTextField(placeholder: "username", value: $username)
            DefaultTextField(placeholder: "password", value: $password)

            Spacer()

            FillButton(title:"Sign In"){
                ///// IS THIS CORRECT?
                self.viewModel.email = self.username
                self.viewModel.password = self.password
                //------------------
                self.viewModel.signin()
            }
        }.padding()
    }
}

The view model is something like:

class SignInViewModel:ObservableObject{

    var username:String? = nil
    var password:String? = nil


Solution 1:[1]

I think we can simplify it with below code.

class SignInViewModel: ObservableObject{
    @Published var username = ""
    @Published var password = ""
}


struct SigninView: View {
    @ObservedObject var viewModel = SignInViewModel()

    var body: some View {
        VStack(alignment: .leading, spacing: 15.0){

            TextField("username", text: $viewModel.username)
            TextField("password", text: $viewModel.password)

            Spacer()

            Button("Sign In"){
                print("User: \(self.viewModel.username)")
                print("Pass: \(self.viewModel.password)")
            }
        }.padding()
    }
}

Solution 2:[2]

Here is possible approach (of course I don't have your custom views, so just replaced with standard, but the idea is the same). Tested with Xcode 11.2/iOS 13.2

class SignInViewModel: ObservableObject{

    @Published var username:String? = nil
    @Published var password:String? = nil
}


struct SigninView: View {
    @ObservedObject var viewModel = SignInViewModel()

    var body: some View {
        VStack(alignment: .leading, spacing: 15.0){

            TextField("username", text: Binding<String>(
                get: {self.viewModel.username ?? ""},
                set: {self.viewModel.username = $0}))
            TextField("password", text: Binding<String>(
                get: {self.viewModel.password ?? ""},
                set: {self.viewModel.password = $0}))

            Spacer()

            Button("Sign In"){
                print("User: \(self.viewModel.username)")
                print("Pass: \(self.viewModel.password)")
            }
        }.padding()
    }
}

backup

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 Satyam
Solution 2