Kotlin Multiplatform Projects + Kotlin/Native = upcoming alternative to Xamarin?

With Kotlin 1.2 JetBrains introduced new experimental Kotlin Multiplatform projects which allow you to write common code once and target different JVM-based platforms such as Android, desktop, or even web. Kotlin/Native is another technology from JetBrains worth noticing. This is a technology for compiling Kotlin to native binaries without the need to use any VM. Kotlin\Native allows you to target iOS, macOS, and embedded systems.

 
 
 

Let’s put this together and create a cross-platform mobile app for Android and iOS with Kotlin! We will create an image browser for one of the most popular websites at NASA, Astronomy Picture of the Day.

 
 
 
 
 
Image
 
 
 
 
 
 
 

Build system and IDE

 

Multiplatform projects have to be built with Gradle, other build systems are not supported.

 

There is a template for multiplatform projects in IntelliJ 2017.3 and Kotlin\Native plugin for AppCode 2018.1.1. However, I’ve used Android Studio for the Android and common code and Xcode for the iOS part.

 

 

 

Project structure

 
 
 

Android\iOS

 

These are regular Android and iOS apps. In the case of iOS, this is an Xcode project that uses the platform-ios module as a generic obj-c framework. In the case of Android, this is a conventional app with a dependency on the platform-android module.

 
 
 

Common

 

This is a shared module and contains only Kotlin classes with no platform-specific dependencies. It can also contain interface\class declarations (without implementation) of platform-specific code. Such declarations allow using platform-dependent code in common modules and are marked with expected keywords. Platform-specific modules (platform-android and platform-ios) use the actual keyword in concrete implementations.

 

Common modules can only use Kotlin language with its common version of stdlib, kotlin-stdlib-common which seems to be quite limited.

 
 
 

Platform-Android\iOS

 

Platform-android and platform-ios contain both the platform-dependent implementations of declarations in the common module for a specific platform and other platform-dependent code. Every single platform module is always an implementation of a single common module.

 

 

 

Project creation

 

All of the projects except the regular iOS app were created in Android Studio using the project creation wizard. Next, I’ve manually edited gradle build files as follows:

 
 
 

Common

 
/common/build.gradle
 

apply plugin: ‘org.JetBrains.kotlin.platform.common’

 

dependencies {   implementation “org.JetBrains.kotlin:kotlin-stdlib-common:$kotlin_version” }

 

kotlin {   experimental {       coroutines “enable”   } }

 

Here we can see the only dependency is on kotlin-stdlib-common hence we are limited to it within this module. The second thing worth noticing is the coroutines “enable”. This statement is not needed to create a common module however I’ve decided to use another Kotlin’s experimental feature. Coroutines for asynchronous and non-blocking programming. I’ll provide a little bit more details in the chapters below but please keep in mind this is not the subject of this article.

 
 
 

Platform-android

 
/platform-android/build.gradle
 

apply plugin: ‘com.android.library’ apply plugin: ‘kotlin-platform-android’

 

dependencies {   expectedBy project(“:common”) }

 

Here we can see that platform-android is a regular Android library (com.android.library) with the actual declarations mechanism (kotlin-platform-android). The expected statement tells the compiler where classes marked with expected keywords are declared.

 
 
 

Platform-iOS

 
/platform-ios/build.gradle
 

dependencies {   classpath “org.JetBrains.kotlin:kotlin-native-gradle-plugin:$kotlin_native_version” }

 

Here Kotlin/Native comes into play.

 

apply plugin: ‘Konan’

 

.. and it’s a plugin for Gradle. Konan allows compiling, building, and referencing libraries. In general, perform all actions needed to build an app.

 

konanArtifacts {

 

  framework(‘AstronomyPictureOfTheDay’, targets: [‘iphone’, ‘iphone_sim’]) {       enableMultiplatform true       enableOptimizations true       enableDebug true       dumpParameters false       measureTime false   } }

 

dependencies {   expectedBy project(‘:common’) }

 

Here we’ve got some Konan configuration section, defining the output framework’s name, targets, optimization, etc., and the same expected statement as in platform-android. In this case, we tell Konan to build an AstronomyPictureOfTheDay.framework for both iPhone and iPhone simulators with debugging and optimization enabled.

 
 
 

Android

 
/droid/build.gradle
 

dependencies {   implementation project(‘:platform-droid’)}

 

Nothing really special here, only dependency on platform-android.

 
 
 

iOS

 

iOS app is a little bit special as we cannot build it with Gradle. This is a regular XCode project with AstronomyPictureOfTheDay.framework included as an embedded library.

 

The dependency tree looks like this:

 

 

 

Coroutines

 

I’ve decided to use a contemporary `async\await like` approach to async programming across this cross-platform project. Unfortunately, Kotlin’s coroutines are not yet supported by Kotlin/Native and we need to introduce a common API with platform-specific implementations. We will use expect \ actual keywords here for the first time.

 

Here’s the common API: 3361b4c

 

Android-specific implementation: a0655fd

 

iOS-specific implementation: 7781417

 

For more  about coroutines please visit: 


https://youtu.be/_hfBv0a09Jc

 

 

 

Data Access Layer

 

Endpoint: https://api.nasa.gov/planetary/apod?date=[date]&hd=true&api_key=[api_key]

 

Response:

 

{      “date”: “2018-09-09”,    “explanation”:”..”,    “hdurl”:”https://apod.nasa.gov/apod/image/1809/CrabNebula_Hubble_3864.jpg”,    “media_type”:”image”,    “service_version”:”v1″,    “title”:”M1: The Crab Nebula from Hubble”,    “URL”:”https://apod.nasa.gov/apod/image/1809/CrabNebula_Hubble_960.jpg” }

 

Corresponding common data model: 2557424

 

Querying and parsing APOTD API is performed in platform-specific code with Retrofit\Gson on Android: 5033119 and Alamofire\SwiftyJSON: 47b922b, 70cbb4e

 

 

 

Data representation

 

As you’ve probably noticed String was used to representing a date in ../apotd/dal/PictureOfTheDayRepository.kt. This is not the most convenient approach as we will have to add\remove days to get the next previous pictures. However, due to the limitations of Kotlin-stdlib-common, we have to create our common date representation with platform-specific implementations.

 

Common date representation: 2145ae2, Android-specific implementation based on JodaTime: c000d54, and iOS-specific implementation based on NSDate: 6e16950.

 

Let’s dig into the iOS-specific DateUtils class (6e16950) as it is quite unusual. Thanks to the import platform.Foundation.* we can use Apple’s Foundation framework within a module entirely written in Kotlin!

 

       val dateComponents = NSDateComponents()        dateComponents.setDay(dayOfMonth.toLong())

 

It looks weird, doesn’t it? 🙂 The same applies to UIKIt and any other iOS-specific frameworks. Here’s a little bit more about Kotlin/Native Swift\Obj-C interoperability: https://kotlinlang.org/docs/reference/native/objc_interop.html

 

Here: 0f0b00f I’m updating repositories to our brand-new custom date representation so now we can add and remove days.

 

 

 

Presenter and View (basic MVP)

 

In 224f8de I’m introducing rather a simple presenter and view interface following the MVP pattern. The code is self-explanatory.

 

 

 

Tying this all together

 

In e5363eb I’m integrating common logic into the Android client. The same integration is in f625a8a for iOS clients.

 
 
 

Summary

 

Kotlin Multiplatform + Kotlin\Native for mobile app development is no doubt an experimental feature. There’s still a long way until it’s production ready. Current IDEs are not prepared for multiplatform and the developer has to use several different IDEs to build the project and it leads to difficulties in debugging.

 

In contrast to Xamarin and Xamarin.Forms, Kotlin’s multiplatform project focuses on sharing only a core common module with business logic, leaving platform-specific code to platform-dependent modules. This approach has its pros i.e. it does not try to standardize platform dependant needs like I\O, and threading and gives the flexibility to use already existing platform-specific libraries and frameworks.

About the Authors

Picture of Damian Wasilewski

Damian Wasilewski

Project Manager
Business Development Manager

Picture of Tom Soroka

Tom Soroka

Leaware Founder
Business Development Manager

Picture of Carlos Lopes

Carlos Lopes

Marketing Specialist
Business Development Manager

Do you want to know how to 
estimate the budget needed 
for your project?

    Lea-Logo
    clutch
    Dsesignrush
    Google
    Sortlist