Recently, a set of new technologies competes in the mobile developers world in a trendy way to prove their entitlement to be used in developing mobile applications. However, most of these technologies concentrate on the UI part and how it should reflect the data.
We can start with 2015 where both react native framework and flutter framework introduced themselves seriously to the mobile developers community as a cross-platform frameworks. For older mobile developers, the programming style of managing UI parts in these frameworks was slightly different than what they were used to.
A few years later, both Apple and Google broke into the same field by introducing swift-ui and jetpack compose respectively. These new technologies are trending to replace the traditional swift ui kit and classic android views after more than a decade of using them.
So, what is the difference? What is new in these technologies? Are they better than old ones? Should we switch to using them or just keep what we used to do? Bring your cup of whatever and let us try to take a closer look.
To understand the difference, we should go behind the technology itself to the programming paradigm that it uses to manage the UI. Some of these technologies are using a programming paradigm called imperative programming, while the others are using another paradigm called declarative programming. Let us have a short explanation about these paradigms in general, then we can discuss how they are applied for managing UI in the mentioned technologies.
Imperative Vs Declarative programming
Based on Wikipedia, Imperative programming is a paradigm that focuses on HOW to execute, where the developer defines the control flow as statements that change the program state.
On the other hand, Declarative programming is a paradigm that focuses on WHAT to execute, defines program logic, but not detailed control flow.
Still confusing, fine, let's dirt our hands with some codes, assume the example of a common programming problem, suppose we have a list of integers and we need to duplicate it’s items, we can do it in imperative way using a for loop to iterate over the list items and duplicate each one, see the kotlin code snippet below.
Also we may do it in a declarative way using either built-in transformation functions like (map) function or building our own transformation function. In the latter case, the transformation functions provide a level of abstraction, where you can use them to transform the list elements without being aware of the detailed steps about how the transformation is done.
Here, the transformIntList function is wrapping the whole transformation process, when you call this function, you just pass your list and your transformation function, you are not engaged with the transformation process itself.
I guess that you are thinking now like this: “At the end our code is compiled and passed to the CPU in an imperative manner, instructions or step by step code”. That's right, the declarative code actually is an extra layer of abstraction on top of the imperative one.
Mobile UI management
Alright, Now we have a quick look at the difference between imperative and declarative programming, let’s move on to the usage of these paradigms in UI management.
Generally, the UI should reflect the app state. App state in simple words means the data that is needed to draw the UI, once these data are changed, the UI should reflect this change to show the new state.
Let’s start directly with a simple example:
Suppose you have an application with a single screen which contains one button and one label, suppose that the label should show the count of button presses, so we should define a variable (let’s call it “count”) with initial value 0. Whenever the user clicks the button, the “count” variable should be increased, and the label should show the new count value. Here the state of this screen is only the variable (“count”) as it is the only data needed to build the UI.
Now let’s consider that we are using the Android views framework, and we need to build the above app. The button click listener should be something like the following:
Here you should first update the state (increase the counter) and then you have to set the label text to consider the state changes. Clearly, this is the imperative way.
If you scale up your thinking about this case, assume we have a complex screen with 50 views and a complex state containing a lot of data, whenever any part of this data is changed you should update the related views manually.
Now, let’s think declaratively. In declarative UI frameworks like flutter, android jetpack compose and swift UI, the app considers the UI as a function of the app state. It is ok to rebuild parts of app UI from scratch instead of modifying it. These frameworks are fast enough to do that.
So, In declarative frameworks, whenever the state is changed, you do not need to set the related UI manually, just notify the framework and it will destroy the current UI, consider the new state and finally build the UI again with the new state.
In the above app sample, if we considered Flutter as an example, your label definition code should be something like the following:
While your onClickListener should be something like this:
As you can see, here we just increased the counter, without setting the label text, just needed to wrap the counter increment with “setState” to notify the framework that the state has been changed, and it will do the rest.
That's it, simply this is the difference between imperative and declarative UI frameworks. Now which approach is better? Unfortunately, this kind of question is unanswerable in our world. You can make a decision based on the case that you have, what type of UI that you have? What is the relationship between your UI elements and the app states? Is it easy to handle state changes manually then you can go imperatively, or is it more complicated so it is better to delegate it to some declarative framework? Answer yourself and Good Luck.