Why we adopted declarative UIs at Prezzee Mobile
Multiple frameworks have adopted declarative UIs in recent years with huge popularity such as React and Vue.js in the web ecosystem, React Native and Flutter in the cross platform space. Native mobile development came late in the journey with Apple releasing SwiftUI in 2019 and Google with Jetpack compose in 2020. One thing is for sure, declarative UIs have taken the programming world by storm and are here to stay. As technology continues to evolve at a rapid pace, developers must always strive to stay up to date with the latest trends and tools to remain relevant in the industry.
Historically Android and iOS platform have used an imperative programming when dealing with UIs. Imperative programming is a programming paradigm that focuses on how a program should perform a particular task. Developers define how the UI should behave by adding a sequence of task to mutate the Views which renders the expected outcome.
Short sample to demonstrate how the imperative approach works on UIKit using Storyboard.
Storyboard is a XML file that defines how titleLabel and updateButton are render in the screen.
class ImperativeViewController: UIViewController {
@IBOutlet weak var titleLabel: UILabel! // @IBOutlet Links storyboard definition and this swift file
@IBOutlet weak var updateButton: UIButton!
var randomValue: String
override func viewDidLoad() {
super.viewDidLoad()
// mutation to set titleLabel style
titleLabel.textColor = .black
titleLabel.font = .preferredFont(forTextStyle: .largeTitle)
}
// @IBAction links storyboard updateButton action with this swift file
@IBAction func generateRandomNumber() {
updateTitleLabel(with: "\(Double.random(in: 1..<10))")
}
private func updateTitleLabel(with newLabel: String) {
titleLabel.text = newLabel
}
}
Now let’s take a look on how this will be defined using a Declarative approach via SwiftUI. Using a declarative syntax, what you care about is to define what the user interface should do, instead of specifying how to achieve it. The Text section will automatically render any updates necessary when the state changes instead of you manually updating the Text every time the button is pressed.
struct DeclerativeView: View {
@State var randomValue: String = ""
var body: some View {
VStack {
Text(randomValue)
.foregroundColor(.black)
.font(.largeTitle)
Button("Generate Random number") {
// action
randomValue = "\(Double.random(in: 1..<10))"
}
}
}
}
In this code sample, it appears to be more evident what the View’s source of truth is in comparison to the imperative one.
In a declarative style, creating views result in less effort because developers do not have to think on the external triggers mutating the view directly, and instead focus on creating the state that best reflects what the views need and deferred the data mutation at a later stage (state management). This represents a different mental model and it takes few iterations until experienced people with an imperative style get used to a declarative way.
Benefits of adopting a declarative approach?
- Easier to write reusable components — Adopting a declarative approach allows for better code organisation. By not having direct triggers in the Views, it is easier to create UI components which can fit for multiple purposes with less effort on writing the code. UI elements in the low level hierarchy ( Organisms, Molecules, Atoms) are stateless and only depends on the data passed to them.
- The future is declarative for Apple and Google — In the coming years, it is expected that both Apple and Google will continue to shift towards a declarative approach for developing UIs. This is evident in the recent release of SwiftUI for iOS and MacOS, which is a declarative UI framework that allows developers to build responsive and fluid interfaces easily. As more developers adopt this approach, it is likely that we will see a significant shift in how mobile apps are developed and designed. And from the Google side, Jetpack compose is the recommended way to build native UIs and major apps in the store are already migrating its codebases to this modern UI toolkit. (This was written more than 1 year ago. My current opinion on this is that is not the future but the present)
- Easier to onboard engineers with previous experience in other frameworks — Team members with previous experience with declarative framework such as React and Flutter would have an easier transition to SwiftUI or Jetpack Compose as the mental model and concepts apply are familiar.
Here is some of the internal team feedback about Declarative UIs at Prezzee
“Declarative UI with combinations of design architecture patterns like Atomic Design and Event Driven Architecture makes dev process flawless and continuous. (Senior Mobile Engineer with previous extensive experience in React and Flutter)” (Senior Mobile Engineer with previous extensive experience in React and Flutter)
”Having zero experience with Jetpack Compose when I came onboard the team, previous experiences with React and React Native for example helped me put together analogies which made it easier get acquainted with Jetpack Compose. Declarative UI helped the team put together reusable components which in turn lead to beautiful UI for our customer-base. “ (Backend Software Engineer with some experience in Ionic Framework, React Native, ReactJS)
“The adoption of Declarative UI, a design system, reusable components, MVI architecture, clean code architecture, SwiftUI, and composable principles has revolutionised our team’s development process. This has enabled us to create visually stunning and responsive user interfaces more efficiently, produce high-quality code, and deliver products more rapidly with better maintainability.“ (Mobile Engineer with mix experience in Declarative and Imperative UIs)
”Declarative UI simplifies application development by clearly specifying user interface design and features. It also makes the code more readable and understandable and reduces application development process errors. Therefore, the use of Declarative UI gives us a great advantage in the development process of our application.” (Associate Mobile Engineer learning declarative before Imperative UIs)
”I once mainly used xml layout and learn jetpack compose after I joined Prezzee. Both are different thinking way. Like in web development, Javascript has “manipulate Dom“ and “data-drive“ implement ways. I believe declarative UI is more advanced as it split into components that are driven by data and easier to reuse, also our Atom Design and MviViewModel is better way to reflect declarative UI.” (Senior Mobile Engineer with extensive experience at Android imperative UI)
Learnings
More than two years has passed since the team started using a declarative approach in our mobile codebases for everything new that we do and we still have plenty of code using an imperative approach. During this period we have learnings that we would like to highlight.
Here are some of the team learnings that we could have incorporated sooner to have a more immediate impact.
- Define common guidelines — When we first started creating new features we did not have a common guidelines to build them, each developer had a different way to create UI elements. But later on in the process, the team adopted a common practice in other frameworks to simplify and align in the language used. Additionally, new developers can more easily onboard and understand the development process, as they no longer need to learn multiple approaches to creating UI elements. The guidelines have also allowed for faster development times, as developers can more easily reuse code and templates, rather than starting from scratch each time.
- Build a Design System — Starting a Design System early is crucial for consistency in the look and feel of applications. Whilst the upfront cost might be high, it saves time and effort in the long run by allowing developers to use established resources instead of starting from scratch in every new feature been build.
- Keep components up-to-date — Jetpack Compose and SwiftUI are constantly shipping new features and optimising the frameworks. Reserve time in the team to upgrade to newer versions and get advantage of new components.
Conclusion
In conclusion, adopting a declarative approach for UI development has proven to be a game changer for the Prezzee mobile team. It allows for easier code organisation, reusable components, and a single source of truth for UI data, resulting in more efficient and responsive user interfaces. Both Apple and Google have recognised the benefits of this approach and are shifting towards it with the release of SwiftUI and Jetpack Compose. As more developers adopt this approach, we can expect to see an even greater shift in how mobile apps are developed and designed. However, it’s important for teams to define common guidelines and build a Design System early on to ensure consistency and efficiency in the development process. The future of mobile development is declarative, and it’s important for developers to stay up to date with the latest trends and tools to remain relevant in the industry.