Sometimes we tend to get caught up in words we use Observers, Delegates, Notifications, Reactive. Sometimes the only difference between these ideas is where the code ends up in the source files. Perhaps that is where we should start to reason about them. What will make the code clearer? For example, let us look at how we can implement reactive notification in Swift without any library.
protocol ModelDependent {
var state: ModelState { get, set }
}
struct Model {
var dependent: ModelDependent
var state: ModelState {
didSet {
dependent.state = state
}
}
}
class ViewController : UIViewController, ModelDependent {
var state: ModelState {
didSet {
updateViewWithState(state)
}
}
func updateViewWithState(state: ModelState) {
// configure view
}
}
All we need is:
- A protocol to abstract away the model dependent
- The local didSet observer on the ViewController.state property
When the model dispatches the state to the view, it is updated accordingly. The update is triggered by the didSet observer on the ViewController.state property. No external observer pattern is necessary. No subscription, no KVO.
We should always try to channel state through a responsible actor in the system. For example, above, the updateViewWithState method may forward state to some of its sub-components. Letting a sub-component observe above its parent is not necessary, and it entangles the structure of the software.
Figure out what makes the code clearer. Observer patterns often put code in wrong places, such as in the initialization of views defined in closures. It may be boring, but there is nothing clearer than merely a type calling a function on another type. Do not give fancy words more meaning than they deserve; it is all just messaging.