Search code examples
iosswiftmvvmdata-binding

Can I use MVVM without data binding?


I am trying to use MVVM. Upon reading some blogs on MVVM, I found them using data binding techniques like Rx, KVO, Boxing etc. Following are my classes for validating user using MVVM. I have not put any code for data binding. What I do is simply pass the username and password from viewcontroller to my view model class upon the submit button click. The view model consists all the logic for validation. And at the end, it returns the status to the viewcontroller, depending on which the view controller shows some message. I am wandering is this the way to go or should some data binding be used?

SignUpViewController.swift

import UIKit

class SignUpViewController: UIViewController {

    let signupviewmodel = SignUpViewModel()

    @IBOutlet weak var textFieldUsername: UITextField!
    @IBOutlet weak var textFieldPassword: UITextField!
    @IBOutlet weak var textFieldPasswordConfirm: UITextField!

    @IBAction func initialSignUp(_ sender: Any) {
        signupviewmodel.updateUsername(username: textFieldUsername.text!)
        signupviewmodel.updatePassword(password: textFieldPassword.text!)
        signupviewmodel.updateConfirmPassword(confirmpassword: textFieldPasswordConfirm.text!)

        switch signupviewmodel.validateUser() {
            case .Valid:
                showAlert(title: "Valid", message: "success") 
            case .InValid(let error):
                showAlert(title: "Error Validation", message: error)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func showAlert(title: String, message: String) -> Void {
        let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
}

SignUpViewModel.swift

import Foundation

enum UserValidationState {
    case Valid
    case InValid(String)
}

class SignUpViewModel {
    private let minPasswordLength: Int = 6
    private var user = User()

    var username: String {
        return user.username!
    }

    var password: String {
        return user.password!
    }

    var confirmpassword: String {
        return user.confirmpassword!
    }
}

extension SignUpViewModel {

    func updateUsername(username: String) {
        user.username = username
    }

    func updatePassword(password: String) {
        user.password = password
    }

    func updateConfirmPassword(confirmpassword: String) {
        user.confirmpassword = confirmpassword
    }

    func validateUser() -> UserValidationState {
        if (username.isBlank || password.isBlank || confirmpassword.isBlank) {
            return .InValid("Please fill all the fields")
        } else {
            if isNumber(username: username) {
                if isValidPhone(phone: username) {
                    if isValidPasswordLength(password: password) {
                        if passwordsMatch(password: password, confirmpassword: confirmpassword) {
                            return .Valid
                        }
                        return .InValid("Passwords do not match")
                    }
                    return .InValid("Password length doesn't meet criteria")
                }
                return .InValid("InValid Phone")
            } else {
                if isValidEmail(email: username) {
                    if isValidPasswordLength(password: password) {
                        if passwordsMatch(password: password, confirmpassword: confirmpassword) {
                            return .Valid
                        }
                        return .InValid("Passwords do not match")
                    }
                    return .InValid("Password length doesn't meet criteria")
                }
                return .InValid("InValid Email")
           }
        }
    }

    func isValidEmail(email:String) -> Bool {
        let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"
        return NSPredicate(format:"SELF MATCHES %@", emailRegEx).evaluate(with: email)
    }

    func isValidPhone(phone: String) -> Bool {
        return true
    }

    func isBlank(text: String) -> Bool {
        return text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
    }

    func isValidPasswordLength(password: String) -> Bool {
        guard password.count < minPasswordLength else {
            return true
        }
        return false
    }

    func passwordsMatch(password: String, confirmpassword: String) -> Bool {
        guard password == confirmpassword else {
            return false
        }
        return true
    }

    func isNumber(username: String) -> Bool {
        guard let _ = Int(username) else {
            return false
        }
        return true
    }
}

User.swift

import Foundation

struct User {    
    var username: String?
    var password: String?
    var confirmpassword: String?
}

Solution

  • Data binding is the foundation of MVVM. So data binding helps you to keep all the business logic out of Views and work asynchronously with sending and receiving data. It would seem ok that you're passing values to viewModel, but there are cases when you will need to pass values from ViewModels to ViewControllers. For example, you have asynchronous API request which responds some data. In MVVM you would store that data in your ViewModel, but your ViewController needs to be notified that some data was received. Data binding helps to solve those cases with less amount of code. Yes, you can work without data binding just by using callbacks, but that would mean that you have to write a lot of boilerplate code.

    I understand that KVO, Rx and Reactive seem to be difficult. So, you can start with simple libraries which offer only binding functionality, without getting deep into complex stuff. Check out these libraries:

    https://github.com/ReactiveKit/Bond

    https://github.com/blendle/Hanson

    And just for note, the way you coded is more like MVP(Model View Presenter). You can check that design architecture if you don't like reactive programming.