Introduction
In the ever-evolving world of mobile app development, staying ahead requires not only mastering individual platforms but also finding ways to efficiently manage and share code across different environments. Kotlin Multiplatform has emerged as a powerful solution, enabling developers to write shared business logic and, more recently, shared UI components across iOS and Android. In this blog post, we’ll explore the capabilities of Kotlin Multiplatform and Compose Multiplatform, diving into two approaches we’ve implemented in our projects to maximize efficiency and maintain a high-quality user experience.
What is Kotlin?
Kotlin is a modern, statically typed programming language developed by JetBrains, renowned for its concise and expressive syntax. Since its introduction in 2011, Kotlin has rapidly gained popularity due to its ability to improve code readability and reduce repetitive code. Designed with both functional and object-oriented programming paradigms, Kotlin provides a versatile and powerful toolset for developers.
Kotlin’s features, such as null safety, extension functions, and coroutines, enable the creation of robust, maintainable, and efficient code. This versatility allows Kotlin to be used across various domains, including mobile development, server-side applications, web development, and data science. The language’s design philosophy emphasizes simplicity and elegance, aiming to enhance developer productivity and experience.
In the realm of mobile development, Kotlin’s capabilities have made it a preferred language for building high-quality, performant applications. Its robust ecosystem and growing community support make it an increasingly attractive choice for developers worldwide.
What is Kotlin Multiplatform?
The Kotlin Multiplatform technology is designed to simplify the development of cross-platform projects. It reduces time spent writing and maintaining the same code for different platforms while retaining the flexibility and benefits of native programming.
Sharing code between mobile platforms is a major Kotlin Multiplatform use case. With Kotlin Multiplatform, you can build cross-platform mobile applications that share code between Android and iOS projects.
What is Compose Multiplatform?
Compose Multiplatform, developed by JetBrains, is a modern UI framework designed to simplify and accelerate UI development across multiple platforms. Built on Jetpack Compose for Android, Compose Multiplatform extends its capabilities to enable the creation of consistent, high-performance user interfaces for desktop, web, and mobile applications from a single codebase.
Compose Multiplatform leverages Kotlin’s expressive and concise syntax to provide a declarative approach to UI development. Developers can define their UIs using composable functions, which describe the UI in a straightforward and readable manner. This approach not only enhances code maintainability but also allows powerful UI composition and reuse.
One of the standout features of Compose Multiplatform is its seamless integration across different platforms. Whether targeting Windows, macOS, Linux, iOS, Android, or the web, developers can leverage the same core UI components and principles, reducing the need for platform-specific code and streamlining the development process.
To begin with Kotlin Multiplatform use the Kotlin Multiplatform Wizard to create a sample that meet your needs: https://kmp.jetbrains.com
A case of study: Replicate Harvest application.
To explore and test the capabilities of Kotlin Multiplatform and Compose Multiplatform, we developed a clone of the popular time tracking app, Harvest (https://www.getharvest.com). This exercise allowed us to dive into the practical aspects of both technologies and understand their strengths and limitations.
Features developed
Our Harvest clone application includes the following features:
- Time Entry Management: Users can create, delete, and edit time entries.
- Date Handling: The app includes robust date handling features, allowing users to select and manage dates efficiently.
- Navigation: Smooth and intuitive navigation between different sections of the app.
First approach: Kotlin Multiplatform for sharing business logic.
The source code of this approach is available in: https://github.com/orangeloops/kmp-poc-separateui
UI/UX development
To start with, we developed the harvest clone with Kotlin Multiplatform and separate UIs. The UI for iOS was developed in SwiftUI while the UI for android was developed in Jetpack Compose.
SwiftUI is Apple’s modern, declarative framework for building user interfaces across all its platforms, including iOS, macOS, watchOS, and tvOS. Introduced in 2019, SwiftUI allows developers to create UI elements using simple, intuitive code with real-time previews. Its declarative syntax enables the creation of responsive and dynamic interfaces by defining the desired state of the UI.
On the other hand, for android we used Jetpack Compose that provides a declarative approach to UI development, allowing developers to define user interfaces using composable functions. This simplifies the process of creating complex and dynamic UI components.
Project architecture
For the application architecture a MVVM (Model-View-ViewModel) pattern was used. Android native applications already have support for viewModels, but for Kotlin Multiplatform there are some changes to make. First of all, the standard viewModel library of Android is replaced by KMP-ObservableViewModel (https://github.com/rickclephas/KMP-ObservableViewModel). The main difference between them is the way state is declared:
The annotations @NativeCoroutines and @NativeCoroutinesState must be added to viewModels:
Other minor differences are the libraries used, in Kotlin Multiplatform some of the android viewModel libraries must be changed to KMP-ObservableViewModel libraries, all details are described in ricklephas GitHub repository.
In viewModel the states can be of a variety of diverse types, from strings and numbers to lists or dates. For string states there are a lot of examples, and changing them to numbers is very straightforward, so we are going to look into date states.
When you have a date state and want to use that state in SwiftUI or assign it to a Date type variable, if you try to use it as it is it will raise an error because types are not compatible. Moreover, if you try to use a Date variable from SwiftUI to assign the value of your LocalDate state variable in Kotlin, a similar error will be raised. To convert one class to another the following must be done:
In our Harvest clone project, we utilized several key features of Kotlin Multiplatform, such as ViewModels and date handling. However, there are other powerful capabilities that we did not incorporate but are worth mentioning.
Serialization
Kotlin Multiplatform supports serialization through libraries like Kotlinx.serialization. This enables developers to easily convert objects to and from JSON or other formats, facilitating data transfer and storage. Serialization is particularly useful for persisting data across different platforms or for network communication.
API Consumption
Although we did not implement API consumption in our project—instead, opting to store data in arrays in the Kotlin logic—Kotlin Multiplatform provides robust support for consuming APIs. Using libraries like Ktor developers can make HTTP requests and handle responses in a seamless, platform-agnostic manner. Incorporating API consumption could be a valuable enhancement for the project, allowing for real-time data synchronization and interaction with external services.
Partial conclusions
Before starting with the second approach of sharing both logic and UI between projects there are some conclusions that can be drawn:
- Information available: Most of the information to start with Kotlin multiplatform is in its own webpage: https://kotlinlang.org/docs/multiplatform.html but information of more complex topics and examples is not available there. Although there is some information online there are still a lot of unanswered questions about how to do things in Kotlin multiplatform.
- Share logic: The advantages of sharing code that otherwise would be duplicated in iOS and android applications are obvious. Without two different applications, the time spent on the project should decrease significantly. Furthermore, if you only write the code once, you only correct the mistakes once and you only test your code once.
Second approach: Compose Multiplatform for sharing UI as well
The source code of this approach is available in https://github.com/orangeloops/kmp-poc-sharedui
Compose Multiplatform was released a year after the release of Kotlin Multiplatform and it could only be used for Windows, macOS, Linux, web and of course Android development. It was not until May 2023, two years after the first release of Compose Multiplatform, that iOS support was added. Since then, the support for iOS has been growing, but there is still work to do.
To start with the second approach, most of the code from android UI of the first approach was reused in this project. The first thing was copying all the code from one project to the other, but some changes had to be made to make it work:
- Harvest application has a calendar icon that allows you to return to today’s day. This icon is not available in the default material icons, so you have to import the extended icons of materialUI. In compose multiplatform you cannot import this library, so none of those extended icons are available.
- Managing dates is usually done with java’s libraries, since Kotlin works perfectly with java and android supports java code there is no problem in using those libraries in Kotlin projects, the problem arises when iOS comes into play.
iOS is not compatible with java, so in Compose Multiplatform, the UI cannot have any import of java’s libraries. It is for that reason that Kotlin Multiplatform provides us with the kotlinx.datetime library to manage dates, so all the DatePicker functionalities that used java’s libraries had to be converted to kotlinx.datetime library.
For that reason, the DatePicker code had to be adjusted to make it work in both android and iOS. - Compose Multiplatform main idea is that the UI should look the same across all platforms. In the harvest application there are some screens that have a background color different than white, for example a cream color. When this color is applied to the background of the screen it works perfectly in android but in iOS not so much. There are some borders on top and above of the screen that do not change their color and it is because they are off-limits for Compose, they cannot be accessed by Kotlin code. For that reason, a work-around had to be applied to “eliminate” those borders.
4. In the first approach it was used a library for viewModel’s other than android’s native one. With Compose Multiplatform you should go back to the native one:
https://developer.android.com/jetpack/androidx/releases/lifecycle
Partial conclusions:
- Most of the code from Jetpack Compose can be used in Compose Multiplatform without much effort.
- Some specific components, like the DatePicker, had to be done from zero. Compose Multiplatform is working in new iOS compatible components: https://blog.jetbrains.com/kotlin/2024/05/compose-multiplatform-1-6-10-ios-beta/#compose-multiplatform-for-i-os-in-beta
- Some compose libraries do not have support in their multiplatform versions.
- Some of the UI features from iOS are lost. There is a lot of work to do to make the Compose Multiplatform components look native in iOS applications.
- Many SwiftUI components do not have an equivalent in Compose Multiplatform so many visual components must be done manually, and the UI does not look like an iOS application.
General conclusions:
Kotlin Multiplatform for sharing business logic:
Characteristics | Pros | Cons |
UI | Keep the UI/UX for both android and iOS users. | The UI still must be done twice. |
Centralized logic | You code and test your business logic only once. | – |
Resources | Allows to reduce hours of coding and testing in the business logic. | Both Swift and Kotlin developers are still needed. |
Documentation | Since Kotlin Multiplatform was released in 2020 there are some years of experience and material to look at. | There should be more information about more complex features. |
Compose Multiplatform for sharing UI as well:
Characteristics | Pros | Cons |
UI | Not only is the business logic written only once but also the UI. | The UI/UX for iOS users does not feel native since many components still “look like android.” |
Centralized logic | You code and test your business logic only once. | – |
Resources |
|
Although no Swift code is written you still need a MacOS device to build the iOS code and use the iOS simulator.
|
Documentation | Compose Multiplatform has a lot of support from Google so many people are starting to use the library and generate content and documentation about it. | Since it is a recent project, they are still in Beta for iOS development. The information about iOS support is limited. |
The fact that hours of coding are reduced in the two approaches presented is clear, but the difference in hours between both approaches is not as clear.
Category | Kotlin Multiplatform | Compose Multiplatform |
UI – SwiftUI | 32,5 hours | 0 hours |
UI – Compose | 19 hours | 19 hours + 16 extra hours |
Business logic | 31,5 hours | 31,5 hours |
Total | 83 hours | 66,5 hours |
It’s important to note that there was no prior knowledge of SwiftUI when the project was started.
Although the hours spent on business logic for the second approach were zero, this is because all the logic was done in the first project. In case the second project was done first the number of hours would be different. To make a fair comparison the business logic hours of the table are the same for both projects.
Something similar happens with Compose UI, a lot of work was done in the first project and reused in the second one, so, to make a fair comparison we added the hours of the first project plus the hours needed to the pure Compose Multiplatform code.
Overall, the hours spent on the second approach are less than the hours spent in the first approach. That said, the second approach still needs to improve iOS user experience to maintain the visual that iOS consumers are used to. Which approach is better depending on what you need for your application.
If you’re looking to optimize your mobile development workflow or need expert guidance on your next project, our team at OrangeLoops is here to help. Contact us today to see how we can bring your vision to life with cutting-edge technology.