Search code examples
iosswiftmvvmdata-bindingobservable

Observables bind 2 textfields


So now in the app i'm currently developing I decided to refactor it by moving to the MVVM design pattern. And here it is where I got to know the famous "Observables".
I managed to understand how they work and the importance of their existence when using MVVM, I've read a couple of explanations on the different techniques for the implementation. By techniques I mean:

  • Observables (the one I'm currently using)
  • Event Bus / Notification Center
  • FRP Techinque (ReactiveCocoa / RxSwift)

I've declared my Bindable class like this:

import UIKit

class Bindable<T> {
    var value: T? {
        didSet {
            observer?(value)
        }
    }
    
    var observer: ((T?) -> ())?
    
    func bind(observer: @escaping (T?) -> ()) {
        self.observer = observer
    }
    
}

What I wanted to do is to bind 2 UITextField's (that are inside one of my ViewController's) with the respective ViewModel. Inside my ViewController there are 2 textfields (emailInput - passwordInput) and a 'Log In' button, that I want it to be disabled unless both textfields aren't empty.
For that I've added both textfield's this target:

emailInput.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
passwordInput.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)

Then:

/// Enable / Disable --> Log In button
    @objc func textFieldDidChange(_ textField: UITextField) {
        if (emailInput.text == "") || (passwordInput.text == "") {
            logInButton.enableButton(false)
        } else {
            logInButton.enableButton(true)
        }
    }

But my question is... How could I implement this same thing inside my ViewModel??
And is it possible to do a two-way binding using my Bindable class?

(If more code is needed to solve this, just ask me to and I'll edit the question)


Solution

  • Observable is used to communicate changes from the view model to the view. There is no need for your view model to use the Observable pattern in order to respond to the updates in your text fields. You can provide a simple function setCredentials(email: String, password: String). In this function you can check if those values are empty and set var loginEnabled: Bindable<Bool>. Your view observes the loginEnabled and sets the login button state accordingly.

    struct ViewModel {
        var loginEnabled = Bindable<Bool>()
        var email = ""
        var password = ""
        
        init() {
            self.loginEnabled.value = false
        }
        
        func setCredentials(email: String, password: String) {
            self.email = email
            self.password = password
            self.loginEnabled.value = !email.isEmpty && !password.isEmpty
        }
    }
    

    Then in your view controller you have something like

    
    var viewModel: ViewModel
    
    
    override func viewDidLoad {
        super.viewDidLoad()
        self.viewModel.loginEnabled.bind { value in 
            self.logInButton.isEnabled = value ?? false
        }
    }
    
    @objc func textFieldDidChange(_ textField: UITextField) {
        self.viewModel.setCredentials(email: self.emailInput.text ?? "", password: self.passwordInput.text ?? "")
    }