A sample project using Kotlin Multiplatform to build a currency converter app for iOS and Android with multiple shared modules.

Photo by Florian Klauer on Unsplash

Disclaimer: Kotlin Multiplatform Projects are at an experimental stage in Kotlin 1.2 and 1.3 and not without issues. Using them for production apps is not advised.

Intro to Kotlin Multiplatform

Kotlin Multiplatform provides a mechanism for sharing code among platforms. The key difference from many other cross-platform tools is that it doesn’t compile all code to platform specific code. Instead, we are given full control over what will be shared and what will be native. Generally, we choose to share the business logic and leave the UI be native to the platform for more natural user experience. This allows us to reap the benefits of writing cross-platform solutions without any of the limitations that usually come with it, like their inflexibility when it comes to writing platform-specific code.

This is implemented by using the Common Kotlin standard library to write the shared module and then compiling the code to different target platforms — JVM, JS, and Native. This means then Kotlin/JVM or any Java or Javascript code can’t be used to write the shared code. Accessing the platform APIs is done by using the provided expect/actual mechanism.

This is the short version, for more information check out the official documentation.

Modularization

The motivation for breaking the shared code into modules is to prevent it from growing into one giant module with tons of responsibilities, coupled features, hardcoded dependencies, some low-level utilities, and anything that can be shared. Basically, our app without UI.

Instead we should aim to create many components, independent from one another, providing a very focused set of functionality. Client apps then have complete control over what they depend on and use.

We can modularize our app around features. In the following example, the currency converter is one feature in a much larger system, that is developed independently from other components, e.g. analytics or user management.

We can further develop the currency converter by abstracting away dependencies to external resources and infrastructure, allowing us to e.g. easily switch servers or use different HTTP clients on different platforms.

The architecture followed in this sample is Hexagonal Architecture (a.k.a. Ports and Adapters). Hexagonal architecture isolates the central logic of our application from the outside concerns by placing the inputs and outputs at the edge of our design. This allows us to easily swap their implementation without changing the central logic. For more details here’s an android sample project that applies this architecture with Dagger 2.

Currency converter

The currency converter app provides an exchange rate based on an input from-to currency pair i.e. the rate at which one currency will be exchanged for another.

The app has an exchange rate provider that fetches rates from a data source like a server, a database, or in our case a mock source with hardcoded values. When a conversion is requested the app uses a rate calculator to find a conversion path between the two currencies.

The main entry point for using the converter is the CurrencyConverter component. The converter internally uses an ExchangeRateProvider that fetches exchange rate data and a RateCalculator performing the conversion.

When building the converter the clients are required to plug-in an ExchangeRateProvider implementation to make it fully functional. This is how we allow customizability of our feature.

The source code on GitHub > webfactorymk/kotlin-multiplatform-currency-converter

A high-level diagram of the solution

Modules

currency-converter-core — The core module contains only the domain logic without implementation.

— The core module contains only the domain logic without implementation. rate-provider-mock — A mock implementation of a rate provider with dummy exchange rates. Works on all platforms.

— A mock implementation of a rate provider with dummy exchange rates. Works on all platforms. rate-provider-api-ktor — (TBD) Ktor implementation of a rate provider. Ktor is a multiplatform Http client so we can use this module for all target platforms.

Ktor implementation of a rate provider. Ktor is a multiplatform Http client so we can use this module for all target platforms. rate-provider-api-retrofit — (TBD) Retrofit implementation of a rate provider. This is a great library but it targets Java and Android. This means that for the other target platforms we can not use this module.

Retrofit implementation of a rate provider. This is a great library but it targets Java and Android. This means that for the other target platforms we can not use this module. ios-configuration — At this version (Kotlin 1.3) the iOS app supports only one K/MPP module. To bypass this and achieve a multi-module app this module will include all modules for the iOS application and be added to the iOS app.

Usage in client apps

For complete reference see the JavaDoc.

Screenshots