The future of apps:
Declarative UIs with Kotlin MultiPlatform (D-KMP) — Part 1/3
A 3-part article explaining the new D-KMP architecture, based on DeclarativeUIs, Kotlin MultiPlatform and MVI pattern.
Year 2020 has not only been the year of the pandemic, but also the year which set the division between The Past and The Future in apps development.
The Future will already start in 2021 and it will be about DeclarativeUIs and MultiPlatform, changing forever the way apps are architected and implemented. It will become normal to build apps for all platforms, which share 85% of the code and have the latest native UI/UX. Development productivity will massively improve, and apps quality too!
In this article we’ll go through the main concepts, also how everything is coming together nicely.
The “past” is what app development has been until now, where most companies have built separate apps for each platform (Android, iOS, Web), with no shared code on the client-side.
In order to limit the amount of code being replicated for each platform, most apps tend to be “thin client”, delegating most business logic and data processing to the only part that is really shared: the webservices.
In this way, webservices tend to be “UI-oriented”. Architectures are designed in a way that most clicks trigger an API call, which provides the exact information to be displayed on the next view, with very limited client-side logic.
Any extra client-side logic requires a replication of the same code on each platform, so this is something that is typically avoided, unless it brings a meaningful benefit for the user experience.
In terms of client-side shared code, there have been several companies that attempted some strategies in these years, but for the most part it’s a history of failures, with many reverting back to native-only/platform-specific development. There are famous examples such as DropBox (moving away from shared C++ code) or AirBnB (moving away from ReactNative). In other words, there haven’t been suitable technologies to allow companies to make a safe long-term investment in shared code.
In this 2020, we are experiencing the rise of two important paradigms, which are happening in parallel: Declarative UIs and Kotlin MultiPlatform. This will bring opportunities never seen before, and it will make “Multi-Platform” and client-side shared code the preferred choice for apps development.
DeclarativeUIs fit perfectly in a multi-platform architecture, because they are stateless and can be fully decoupled from the business logic. By combining Declarative UIs with Kotlin MultiPlatform, we can safely architect apps with a huge amount of client-side shared code (up to 85%), performing as perfectly-native on each platform. At the same time we can have the latest native UI/UX on each platform.
Webservices can now be fully “UI-agnostic” and focus on providing the data in the most normalized way, removing any redundancy, as all data processing and formatting can be done at client level. This can also greatly improve data consumption.
But let’s go on with order. Let’s start by defining what are the pillars for the upcoming era of apps development.
The 3 pillars for the future of apps
- Declarative UIs (JetpackCompose on Android, SwiftUI on iOS)
- KMP (Kotlin MultiPlatform)
- MVI pattern (Model-View-Intent)
We call this architecture D-KMP, standing for DeclarativeUIs with Kotlin MultiPlatform. The MVI pattern is what makes the two work best together.
The D-KMP architecture
It’s important to clarify that the D-KMP architecture we are presenting is aimed at greenfield projects.
We are not talking about how to gradually introduce Declarative UIs and Kotlin MultiPlatform to an existing project.
Our goal is a clean, solid, and future-proof architecture, which doesn’t make compromises with the past, and it builds on the most innovative technologies and paradigms.
It’s also important to highlight that D-KMP is not a library, but an architecture, fully relying on the official frameworks.
Let’s now go in more detail about each of the 3 pillars of the architecture, starting with the DeclarativeUIs.
Declarative UIs have landed on Android and iOS!
After over a decade, we are experiencing the most important revolution for the mobile frameworks. Both Android and iOS have started to ship their new UI toolkits, which are both declarative, taking inspiration from the paradigms of React and Flutter. They are going to fully replace the current way of defining views in both operating systems.
Google announced JetpackCompose at Google I/O 2019. It entered Alpha stage in August 2020. It is expected to reach Beta in spring 2021, and version 1.0 by the end of 2021.
JetpackCompose is supported by any device with Android 5 and later (target API 21). This means that any new JetpackCompose API will be backward compatible, and won’t require the new Android version. This is because JetpackCompose operates low level, by drawing directly on the Android view canvas.
Apple announced SwiftUI at WWDC 2019, which was already shipped in 2019 with iOS13. There were further improvements this year with the iOS14 release.
Unlike JetpackCompose, SwiftUI is tied to the iOS framework for updates. New SwiftUI APIs will not be backward compatible. However, considering that all devices supporting iOS13 are also supporting iOS14 (unusually, Apple didn’t deprecate any device this year), we can safely target iOS14 for apps using SwiftUI.
Why declarative UIs?
JetpackCompose and SwiftUI are both Declarative UI frameworks, which means they are just describing how the UI should look like for the different states, without managing the state directly. The Declarative UI paradigm have become popular thanks to frameworks like React.js and Flutter, which have shown how simpler it is to interact with stateless components. Their success has eventually pushed both Android and iOS to join the Declarative UI world!
With JetpackCompose, you can forget about the cumbersome Android view-based system with the dreadful Fragments. And with SwiftUI, you can forget about the UIKit ViewControllers and the inflexible Storyboard. It’s a clean start. It’s the Future!
The nature of Declarative UIs allows a very neat separation between the UI Layout and the ViewModel, as no extra layer of code (using findViewById and @IBOutlet) is needed to connect the two. On this topic, Leland Richardson by Google wrote an interesting article, explaining the importance of maximum cohesion and minimum coupling.
JetpackCompose and SwiftUI are very much alike. There are trivial differences about the syntax (JetpackCompose uses Kotlin, SwiftUI uses Swift) and the navigation patterns, but the concept behind is exactly the same. And above all, the data to be fed into these two stateless UI frameworks is exactly the same. Because of this, it makes a lot of sense for a ViewModel to be platform-independent! We’ll talk more in detail about this further on.
DeclarativeUI for Web
The most famous DeclarativeUI on the Web is React.js (created by Facebook), which is actually the framework that brought the DeclarativeUI paradigm to success. It was an industry-changing framework. Without the success of React.js, now we probably wouldn’t have DeclarativeUIs on Android and iOS.
Kotlin conveniently provides wrappers for React.js, which allows us to use Kotlin/React as our DeclarativeUI layer on the Web. It can be plugged into our D-KMP architecture, in the same way as JetpackCompose on Android or SwiftUI on iOS.
Beside Kotlin/React, what’s probably even more interesting is Compose for Web, which is the web version of JetpackCompose, currently being developed by JetBrains (Kotlin’s creators). If a Kotlin/React UI implementation would require an extra 15% of work (as opposed to the 85% of shared KMP code), Compose for Web would presumably require much less than that, as it would be very much the same of JetpackCompose for Android. So we definitely look forward to that.
DeclarativeUI for Desktop
In the meantime, while we wait for the Web version, JetBrains announced this month (November 2020) the preview version of Compose for Desktop, which allows the development of desktop apps on Windows, macOs and Linux.
In terms of desktop, it’s important to note that SwiftUI has already macOS support out of the box. Any UI you write with SwiftUI is seamlessly adapted to iOS, macOS, tvOS, watchOS. We can expect the same seamless adaptation from JetpackCompose on all platforms (Android, Desktop and Web).
It’s easy to envision a near future where JetpackCompose and SwiftUI will be the only 2 UI frameworks you need to be able to write first-class apps on any platform.
Why not just 1 UI framework for all platforms?
You might wonder if we can expect a single DeclarativeUI framework to be working for all platforms.
In other words, will JetpackCompose eventually support all Apple operating systems (iOS, macOs, tvOS, watchOS), and will SwiftUI eventually support non-Apple devices?
As far as we can see, if this will ever happen, it will not be done by either Google or Apple, but by third parties or by the community. We can already see that JetpackCompose is being ported to Desktop and Web not by Google, but by JetBrains.
We can expect Google and Apple to keep focusing on their own operating systems. However, this is very healthy for the future of UI/UX. It means that we’ll keep having two strong and independent toolkits, competing for innovation.
Let’s now focus more on Kotlin MultiPlatform:
go to the Part 2 of this article!