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 a need to use any VM. Kotlin\Native allows you to target iOS, macOS and embedded systems.

 
 
 

Let’s put this together and create 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.

 
 
 
 
 
 
 
 
 
 
 
 

Build system and IDE

 

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

 

There is a template for multiplatform project 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 iOS part.

 

 

 

Project structure

 
 
 

Android\iOS

 

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

 
 
 

Common

 

This is 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 module and are marked with expect keyword. Platform specific modules (platform-android and platform-ios) use the actual keyword in concrete implementations.

 

Common modules can only use Kotlin language with it’s 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 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 coroutines „enable”. This statement is not needed to create common module however I’ve decided to use another Kotlin’s experimental feature. Coroutines for the asynchronous and non-blocking programming. I’ll provide a little bit more details in chapters below but please keep in mind this is not the subject to 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 expectedBy statement tells the compiler where classes marked with expect keyword 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 plugin for Gradle. Konan allows to compile, build and reference 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 expectedBy statement as in platform-android. In this case we tell Konan to build an AstronomyPictureOfTheDay.framework for both iPhone and iPhone simulator 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 a embedded library.

 

Actually 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 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

 

 

 

Date representation

 

As you’ve probably noticed String was used to represent a date in ../apotd/dal/PictureOfTheDayRepository.kt. This is not the most convenient approach as we will have to add\remove days to get next\previous pictures. However due to limitations of kotlin-stdlib-common we have to create our own 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 are able to 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 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 are able to add and remove days.

 

 

 

Presenter and View (basic MVP)

 

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

 

 

 

Tying this all together

 

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

 
 
 

Summary

 

Kotlin Multiplatform + Kotlin\Native for mobile app development is no doubtly an experimental feature. There’s still a long way until it’s producton ready. Current IDEs are not prepared for multiplatform and 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 it’s pros i.e it does not try to standarise platform dependant needs like I\O, threading and gives a flexibility to use already existing platform specific libraries and frameworks.

About the Authors

Damian Wasilewski

Damian Wasilewski

Project Manager
Business Development Manager

Tom Soroka

Tom Soroka

Leaware Founder
Business Development Manager

Carlos Lopes

Carlos Lopes

Marketing Specialist
Business Development Manager

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

quote-svgrepo-com
quote-svgrepo-com

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *