Swift + MVVM + Two Way Binding = Win!
TLDR; A way to achieve two way bindings on a UIControl and Observable, so that you write the code, you want to write, not the code you have to write.
MVVM? why not MVC that Apple recommends? Android does MVP great right? How about the cool VIPER pattern? I believe great efforts have already been put to explain what each pattern brings to the table and so the idea here is not to add to the debate but merely build on top of the opinion I have already formed: MVVM is the way to go.
As a quick primer to what MVVM is, it is a design pattern whereby the ViewModel mediates between a data providing Model and View which displays the data provided. Kind of like the following diagram:
in iOS, View is essentially a ViewController and ViewModel is an object (a structure) which provides exact data for the view to render.
This provides a loosely coupled architecture which is maintainable ( very thin view controllers ) and testable (ViewModel abstracts out the UI and hence is easily testable).
There is still a caveat though: classic MVVM allows for single responsibility principle easily (and beautifully) in case of models as domain models. However, in case of anaemic models ( which is generally the case when you have well written REST APIs), one would also need another Mediator or Presenter which facilitates data and navigation flow.
Now, View Model has responsibility to update View as well as get updates from View regarding the changes made by the user. This can be achieved by minimum code using bi-directional data binding.
But …
I was bit stumped here, with KVO bindings as first class feature from time unknown, I was expecting two way bindings to be available organically too. the alternatives are available though, thankfully, like SwiftBond, or full fledged reactive libraries like RxSwift, RxCocoa.
Since two binding is a very tiny spec in paradigm of Reactive Programming, unless I am working with streams, I believe its a heavy dependency to have in the application and instead, explored what it takes to have two way bindings working cleanly and quickly.
The Observable
Two way binding is essentially a Observer-Listener (or Pub/Sub) pattern in a bi-directional setup. The two participants, generally a Control and a DataProvider are bound to each other such that on change of values, the corresponding component’s listener is notified with value changed.
Firstly, we need the Observable to provide the above pattern.Consider following gist:
A simple generic class Observable which notifies all the “observers” whenever its value is set. note: The nullability is more to signify unset condition than actual null value to the observed data.
Bindable
Now that we have Observable, we can define a protocol which can be conformed by UI Controls (or any object essentially, or you can create your custom controls ) which will allow you to have the bi directional setup.
In the above gist, you have a Bindable protocol which has default implementation which uses an internal binder Observable object (objc associatedobjects FTW! ) which is set on control state change detected.(shout out to https://github.com/cprovatas for Selector based action approach. very nifty!)
UIControls + Bindable
Now that you have Bindable and Observable in place, lets have common control’s extension and get them ready for action:
Since we have already defined a default implementation: All we need to conform now to is the way each control updates and exposes its values using updateValue and observingValue methods. Pretty Simple!
A Sample:
Let’s put our mini framework to use. Let’s assume we are making a form. Firstly, we design a ViewModel which encapsulates our needs. All the properties that we need to monitor are wrapped with Observables. Like below (ignore few pretty get functions, they are BAU methods :) ) :
With View Model in place, we go ahead and create our view controller. Now below is all the code that you need to have your form working :
setupBindings , on viewWillAppear binds our control to respective view model properties and now on all the changes are bi directional.
Don’t believe me?
That’s it! The entire project is available at: https://github.com/manishkkatoch/SimpleTwoWayBindingIOS
the SimpleTwoWayBinding is available as CocoaPods v0.0.1