Following yesterday’s post and “heated” thread discussion, which was really helpful, I am now presenting a new diagram, which hopefully clarifies the points and removes the confusion.
@jw made very meaningful observations, by pointing out that architecturally there is no difference at all between Declarative and Imperative UIs. You can apply MVI/unidirectional data flow in exactly the same way, for both.
My yesterday’s diagram was indeed confusing, by hinting a unidirectional data flow just for the Declarative UIs.
But there is an important difference between Declarative and Imperative UIs, and that is cohesion. In Declarative UIs, you just have 1 layer (Compose/SwiftUI), while in Imperative UIs, you have 2 layers (one of Kotlin/Swift code, and one of XMLLayout/InterfaceBuilder). This also means that each UI component introduces an extra relationship (coupling) between the 2 layers, which needs to be managed, with extra boilerplate code.
Android has been trying to minimize this problem by introducing the ViewBinding, but the same mechanism doesn’t exist in iOS InterfaceBuilder, where the coupling between the 2 layers needs to be kept updated manually.
Declarative UIs don’t have this low-cohesion issue, as the data can be applied directly on the UI layout, without an extra middle layer. This removes a lot of boilerplate code and clearly speeds up development.