Kotlin multi-platform projects structure

Previously, we talked about how to share code across platforms in kotlin world. But as your code base grows, you will encounter a scenario where some code in the projects is cross, but some other is platform specific, so will not be shared and there may be a version of this code for any or all platforms. Things like separating the platform specific implementation to another repo in order to reuse them in a future project. Well, lucky you, this page covers that topic.

1. Overall structure

  • Project platforms
    • This repo contains the platform implementation. The repo provides you with a universal API across platforms as well as the platform implementation.
    • This is the code base where you need to implement each API twice or thirds depends on how many platforms you want to support.
  • Project lib
    • This is the `common` code which you want to share across all platforms without any change. Such that, you can invoke `UserService.findAll()` to retrieve all the users no matter which platform you are working on.
    • This code will rely on `project platforms`. For instance, an API like `UserService.findAll()`, it needs to do an HTTP call where the actual implementation of sending an HTTP request is different across platforms. While using `project platforms`, this `project lib` can purely focus on the core business logic.
  • Project app
    • This is could be a native app which will call `project lib` for preparing all the data, while it only needs to focus on the UI and UX. Or maybe some native platform-specific feature.
This is a reasonable approach, at least to me. Where every `project` could focus on their own business.

2. Setup the project platforms

This is the easiest one. Because it’s just a typical kotlin multi-platform project setup. You can read the official document for more detail.
It should contain the platform implementation and the related tests.
The folder structure is straightforward:
  • common: for `common` code which contains all the `expect class`.
  • platforms:
    • jvm: contains `actual class` implementation for a jvm
    • js: contains `actual class` implementation for a js

3. Setup the project lib

The folder structure is like:
  • common: business logic that we want to share across platforms
  • bundle
    • jvm: Where we actually test and build the `common` for the `jvm` platform.
    • js: Where we actually test and build the `common` for the `js` platform.
This is one is a little bit tricky because we want to make it `common`. To things needs to take care here are:
  1. We need to add the `common` part from `project platforms` as dependencies.
  2. We want to test this `common` business logic.
  3. When we compile, we need to combine the platform implementations from `project platforms` along with the `common` to yield a platform-specific package.
First, let’s solve them one by one:
1. Add `common` part as dependencies, I tried many ways, including adding the generated `jar` file or `sources-jar` file. Always something wrong here, either the auto-completion stops works, or the internal member can’t be recognized while the namespace could be recognized. Only 2 ways work:
  • Just embed the source code from `project platforms :common` in you `sourceSets`: `main.kotlin.srcDirs += “$kotlin_idiom_path/interfaces/main”`
  • Add that common module as a subproject and add it to the dependencies.
  • I prefer the 1st one because now your gradle settings won’t contain any noises. It will benefit the side-panel of gradle in IDEA as well.
2. In terms of the test, you just write the tests in this `common` folder. But you can’t run the tests, you need to run them against the certain platform, otherwise, they are `common` code, which platform for them to run?
3. This is where the `bundle` coming into play. For building and testing. Let’s take `jvm` for example.
  • First of all, you don’t need to add any code here, this folder, as its name indicates, just for bundling code together. So, under in `:bundle:jvm` subproject, it only contains a `build.gradle` file.
  • You need to use `kotlin-platform-jvm` to compile this module
  • In the `sourceSets` setting: you need to add the source code from all the 3 places, both `common` modules from `project platforms` and this `project lib`, the source code of platform implementation from `project platforms`.
  • You need to add the tests only from `common` module of `project lib`, such that you can run the tests. And it won’t run the tests from `project platforms`, because they all gonna take care there. You don’t need to worry about that. Now run the `gradle :bundle:jvm test` will run the tests.
  • Why I add the source code rather than use the `jar` file? Well, hard lessons learned, this is the only way currently.

4. The end

Now run the `:bundle:jvm build`, it will build a lib to that platform. Try to consume it, it works really well. 🙂

Kotlin code sharing among platforms

When you try to deal with the across platform codes, you need to solve 2 things, one is what to share, the other is the architecture of how to share the code.

Different languages have different techniques to the second problem, but in Kotlin, you can use multiplatform projects to share the code.  With kotlin native, you can even expand the support to natively to iOS and Android.

1. Goals

  • share code among JVM, JS, iOS, and Android.
  • enable the use of XCode and Android Studio to edit the native app, while using another IDE to edit the sharing code part. (But you can use Android studio for editing the KN code as well because they use the same language and the setup will be ready.)
    • I saw some setups. They use root folder as a whole gradle project which will affect the Android project structure been displayed in the android studio because it will add some noise there.
  • share more code between iOS and Android.
    • The main hurdle is the platform-specific API. We can make them as `interface` and still shared the logic around them. But here we will use `kotlin multi-platform` approach to make the code more elegant.
    • Think of an API like this `UserService.getAllorNull()`, then your native side just need to bind the result to the UI. Even though the `HTTP` call is different across platform. But all the exception handling and data processing around the http call are the same and could be shared.
  • a clean structure where everything related should reside together.
    • I mean for the gradle phase as well. Such that, `android` folder is only for Android, `ios` folder is only for iOS. The root folder is for all projects. And the gradle settings for `KN-android` should be in the `android` folder as well as the `iOS` side.
  • enable testing the Kotlin native code as well.
    • It’s native project so anything native can be tested via the strategy on that platform. But what about kotlin native part, what about the `common` code? What about the kotlin native platform specific code for `android` and `ios`? Yes, we have that part covered as well here in the example.

1.1 What if there is no need to care about the platform-specific implementation?

If all you need is the API from kotlin-stdlib, and no platform specific code, then you don’t need to use this setup. You can simply remove the platform folder. And let the common just output an iOS framework for the XCode project to consume. For Android, embed the source code in Android studio to make it as dependency, then it’s fine. Same case for JVM and JS as well. And the setup for JVM and JS is as easy as following the official document.

2. Folder structure

  • ios: XCode project
  • android: Android Studio project
  • common: Common code that is about to share across platforms without any change.
  • platforms:
    • ios: platform specific API for iOS
    • js: platform specific API for js
    • jvm: a dependency for both `android app` and `jvmApp`

Very easy to understand. One interesting thing here is in the platforms folder, besides the common folder. What’s the platforms/android and platforms/ios folder for? Well, they are for platform-specific API.

2.1 How to consume the lib

  • JVM and JS just as easy as including the lib as the dependencies in build.gradle.
  • Android related code will be shared via simply including the according module in the android project.
  • iOS related code will be shared as an iOS framework because Kotlin native will generate that. We will then add it to the XCode building phase. You can see more details on my another [blog](http://www.albertgao.xyz/2018/01/14/how-to-create-kotlin-native-ios-project/).

2.2 Background knowledge for Kotlin multi-platform project

Let’s say you want to have an HTTP kotlin class where you can do the normal GET, POST thing, but the HTTP implementation is different across platforms. If you use the old school interface way, you need to declare an IHTTP interface, then implement it twice in android and ios. Then inject it at runtime to make it work.

But via using the Kotlin multi-platform project, you don’t need to inject, the compiler will only pick up the piece of codes depends on your building target. If you build for Android, it will only grab the Android implementation.

It currently works for jvm, js and kotlin native, oh, that’s a nearly-all-platform code sharing tech right? 😀

And it’s very simple.

You need to expect an implementation of your common code.

expect class Platform {
    fun get():String

Then implement the actual code in somewhere else, like android.

actual class Platform {
    actual fun get():String {

Or in ios:

actual class Platform {
    actual fun get():String {

Then with some proper setup in gradle, it will work flawlessly.

2.3 How to code the `platforms-ios`

Oh well, you may think that old school interface may better in terms of iOS because you need to implement the iOS API specific thing in swift or obj-c then pass it back to kotlin native. But that is not true. Because there is a 1-to-1 API match in kotlin native such that you can implement the iOS specific API in kotlin as well. And it has the same signature which means you can reuse all the existing knowledge. And kotlin and swift are very similar. And from v6, the building phase will link some of the iOS API (you need to build the others) for you if you are on a Mac.
Which means in an ideal world, your swift side just needs to care about the UI part while all other logic will be handled in the Kotlin native side, shared across platform and tested well.

3. About the example

All the code for setup can be found in this repo.

  • `Sample` class is for code that is sharing across platforms (Which means you have to use API from `kotlin-stdlib-common`).
  • `Platform` class is a class which has been implemented twice for different platforms for showing the platform API case.
  • Open `android` folder from the root in Android Studio, run the app, it will show a string from the `:shared-android` project.
  • Open `ios` folder from the root in XCode, run the app, it will show a string from the `:platforms-ios` project.

In fact, all the apps (jvmApp, jsApp, android and iOS) retrieve the string by invoking the method from the Sample class. And the Sample class invokes the method from Platform class to get the string. When you build an iOS framework, the KN compiler will use :platforms-ios to build along with :common. And when you consume it in android project, the setup will use :platforms-android along with :common. No injection, no affection on the code structure, just that simple. Thanks to the Multi-platform project setup from kotlin.
It is a perfect example of showing how to share the code when you have to deal with the platform API.

Hope it helps. Thanks.

Evaluation of Kotlin Native for mobile: January 2018

We have been looking at Kotlin Native (KN) as a viable solution for mobile development, and as a competitive solution to those like Xamarin – or to completely separate developments for iOS and Android. To be a viable mobile solution in general requires Kotlin Native(KN)  to be workable for iOS as first announced

On my inspection, I believe that KN makes sense, and very good sense in the longer term for mobile development, but Kotlin Native is still far from production ready for the following reasons:

  1. This is the most important reason, all the platform specific API hasn’t been published as maven artifacts, which mean you can’t add them as project dependencies in gradle which leads to many other problems like:
    1. syntax highlight
    2. autosuggestions
    3. your code related to KN is fragile as they said:
      • `warning: IMPORTANT: the library format is unstable now. It can change with any new git commit without warning!`
  2. No documentation for API. And without any support from IDE. It will greatly slow down the job.
  3. Multiplatform project needs to support KN in order to get benefits in terms of architecture. You can still just use the old school way (by declaring `interface`). But this should be ready in 0.6 (My feeling according to slack, still not sure.)
  4. In response to a question on the Kotlin Native slack channel “Any ETA on beta/1.0 version?Nikolay Igotti replied( 25/December/2017) :Not yet, however, being v0.x is mostly an internal thing, in general both compiler and runtime are pretty well tested and known to work on rather complex codebases, such as Video Player: https://github.com/JetBrains/kotlin-native/tree/master/samples/videoplayer

  5. CLion: The benefit of using CLion seems more for the KN dev team, and for projects integrating with the C family, which is not the case for iOS projects.  When the developers deal with the cross-platform setup, they need to dig into the LLVM layer in order to build the bridge. CLion is the only IDE in JB family which supports Kotlin Native at this time, which is problematic for projects looking to go multiplatform with JVM or JS, and for iOS projects which combine with Swift and Xcode.  There is no announced plan for supporting the other JB IDEs. Further, from the project leader’s talk in slack in late January 2018 on support for IDEA: `this is not yet completely decided, but CLion is currently the only option`. And you know, CLion doesn’t support gradle, and they use gradle to build…..    The other possibility is Koltin support in AppCode, which there are suggestions may be coming and could be the best solution.
    • So we have some difficult situation here, which is:
      • CLion doesn’t support gradle. And the issue is there since 2014.
      • The multiplatform project hasn’t support KN yet. But this one is easy and difficult.
        • It’s easy because once the maven dependencies is there, the support will be nearly there. Or we could build the whole thing by `gradle` ourseleves.
        • It’s hard because as I said… The KN libs hasn’t been published as maven dependencies yet.
      • And from the talk of slack, it seems that the team holds the releasing mainly because the KN lib has a completely different format, even for the file extention, it’s called `.klib` now. So, uploading it to maven or JCenter seems not ideal. I assume JB might end up with building a new repository just KN libs.

And when there are some problems on both IDEA and CLion, a potential answer from JetBrains might be a new IDE just for Kotlin native. The following video maybe a provenance for this:

https://www.youtube.com/watch?v=QOIn8Uh3lkE at 6:20 Andrey Breslav said (in Russian) they started development of new commercial product for cross-mobile development, Android and iOS.

But it seems that Appcode with KN support should land first according to the slack chat.

The team leader has said in the slack channel that they will ask for dogfooding once it’s ready. 🙂

If app developers wish to only build using the kotlin-std-lib, and inject the platform-specific API at runtime, it’s doable. But then your codebase will be a mess because you need to build the bridge by yourself  as the kotlin type has been converted to some special interface type, which you need to implement in the swift side as well…. all to cope with an interim solution which will be deprecated in a future version release…

So, 3 things are crucial for using KN in production:

  1. Decent IDE support such that we could inspect the APIs signature(No matter CLion or IDEA or AppCode, this is essential)
  2. Multiplatform project support KN
  3. Able to create an KN project without depending on the KN repo…. Which means they need to publish their platform lib in order to enable us to add them as the project dependencies in gradle. Otherwise, a single build on KN repo takes 2 hours…..on my 2017 i7 15″ retina MacBook pro.

All of the 3 are all needed for writing KN related app.

But I will keep an eye on KN, because I think as I dig into more, I think KN starts to make more sense.

  1. You can really share your logic. The most awesome part is that you can invoke platform-specific API from kotlin side which means you don’t need to deal with the communication between languages. Which means you can really embed heaps of logic in the KN base.
  2. The multiplatform project is a really great way to share code across platforms. It just makes sense. You abstract your code in `common`, `js`, `jvm`, `ios` or `android`. And the gradle will grab the related pieces to compile according to the platform you wanna build against.
  3. This sort of embrace-the-platform-differences-rather-than-write-everything-once-and-run-anywhere concept has granted KN a very promising future compare to Xamarin’s replacing-them-all.

Building: No need with python?

Yes, building is needed with python.  but in one special case it can be hidden in the background. This page provides background on building, both with python and kotlin.

It can seem that python programs do not even need building, but the reality is some form of build needed with any program.  The good part of building in python, is building as you develop is so simple you don’t really notice.  The negative is if you later want to package up what you have developed, you may be confronted with one of a wide range of different builds options some of which can be quite complex.

Building Introduction Topics:

  • What is Building?
  • The Two Ways to Build: Environment vs Package Build
    1. Environment Build
    2. Package Build
  • Building in Python
  • Building in Kotlin
  • Conclusion: The different focus of Python and Kotlin

What is Building?

A Definition: Building is the process of putting in place all the components needed for a program, and proving the code with location information for each component.

The components are the resources needed by the program, such as program code for library functions, and images that may be used by the program.

Developing in python, it can seem like there is no such step as ‘building’.  Just type ‘python ‘ and python will do all that is required.  The reality is that this is an environment build approach, and during development any new libraries or other resources are added to the environment. So the steps to prepare the environment happen over time and can be forgotten. It is only when there is a need to run the program on computer other than the development computer, and all the environment install steps need to be repeated, that the environment build gets noticed.

Note also, that reliance on this approach limits the use of python.

In fact while this is true for the simplest way of running python, it is not always true.  That is because the simplest way of running python relies heavily on an ‘environment build’.

The Two Ways to Build

Environment Build vs Package Build

An environment build is where all the resources needed for the program are installed in the environment prior to running the program, enabling different programs to share the same resources.

By contrast, package build is where the resources for the program are packaged inside the program file, ensuring each program is self contained and independent.

1. Environment Build.

For an environment build, it the environment around the program that is built. An environment build means running the program on a new computer will require an ‘install’. The install can either be a series of steps to be followed manually, or there can be an install program to perform those steps.

The advantages of environment build are:

  • no need to re-install resources that have been previously installed in the environment another program(s)
  • during development, each new resource can be installed as required and independently, resulting in only one build for the complete development cycle, spread out over the entire development cycle

Disadvantages of environment build:

  • distribution of the application requires either a manual install process, or building of an installer which can be an additional development step
  • different applications may want different versions of resources to be present in the environment which may cause complex conflicts that are difficult to identify and/or resolve

 2. Package Build

A package build is where the components are combined in a single runnable ‘package’ file containing the components required by the program.

Advantages of package build:

  • distribution of the application can be as simple as just one file
  • each application contains its own components so no version conflicts can occur

java.jar files use a pure package build approach, which does require a build before every run, but means a fully portable self contained program is readily available without extra steps.

Building in Python

Building in python is very different for different uses of python.

Developer is the end user: One of the most common situations with python is the SEAAS developer, who is both the developer of the program and the end user of program. The resources needed by the program are installed as the program is built so there never seems to be a build at all.

Building for python.py files mostly becomes installing python, then ensuring all the imports have all the packages those imports require installed. If the imports all work, the environment for the python.py program has been built.  Once the environment is built, the program can easily be run without further steps.  The disadvantage of the this ‘build the environment’ approach is that for each new version of python the environment to satisfy the all python programs must be rebuilt, or alternatively a virtual environment approach must be adopted for development, and deployment can be problematic.

Web Server: Even if a web server is viewed by millions of people, the application only needs to be installed once per server, and it can be installed by the developer or other very skilled people.  Manual install of the environment for the python code, even if there are several steps, is entirely practical and is done by the developer.

Mobile Applications: Kivy is probably the best mobile framework for python, but like any other mobile app, Kivy apps, like all mobile apps, must be fully packaged.  Building a packaging is not part of standard python workflow, so the specifics of packaging kivy must be learnt and is quite is a complex and often fragile process.

Sharing with other friend/colleague Python Programmers (SEAAS): Simply send the source file, the other programmer probably already has python (probably the right version?) installed.

With simple python programs, there are no other ‘parts’, just python itself and just the one file.  As programs grow, there are imports, and then libraries to install to enable more imports. If the person installing knows pip install, they will quickly satisfy other requirements.  No build by the developer, manual environment build by the end user that is quite straightforward provided the end user can develop in python.

Sharing libraries and/or applications on PyPI: PyPI (or the “cheeseshop”) allows sharing open source python projects ready for simple installation into any computers python environment with a simple ‘pip install’.

Windows / MacOS applications: To distribute an application, to be installed and run by someone who need not know how to program in python, there are a variety of solutions. py2exe and pyinstaller and examples, and in each case the process is equivalent to a build that automates the install for regular users of the program.

Building in Kotlin

The bad new: There is always a build process.

The good news: There are build processes that work for all scenarios and are highly refined.

First, on that bad news.  Even the kotlin “hello world” program needs a build.  The ‘hello world’ program must call a print function, that has to be connected with the program code.  With python, the print function is in the standard library, which was installed in the python environment when python was installed so the developer needs no further build, unless that developer wants to send the program to a friend as a “helloexe” type file……. which would of course need a build.  However for “hello world” , having a “hello.exe” is not needed so you do not notice.

With kotlin, you are going to get the equivalent of “hello.exe”, so you there will be a build. The “hello” code must be built into a file together with the “print” function to be a complete program, and that requires a build.  All kotlin programs are made ready for installation on a computer, without first installing kotlin on any computer which will run the program.  Either as the “hello.exe” with kotlin native, “hello.jar” with the java version or “hello.js” to run in a browser.  Kotlin offers that choice (a level of choice not matched by python) but always requires a build.

Secondly, on the good news: The build is very simple. Because every developer is doing builds all the time, building is highly evolved.  Building can seem complex, because it is very flexible, and to learn all that can be done with build is still huge.  But to learn simple builds, equivalent to the examples for python given above is very easy.  The trap to avoid is the trap of trying to learn all that can be done with builds before getting started.

Conclusion: The different focus of kotlin and python.

Note: What applies here for python, also applies for many other ‘dynamic’ languages.

For someone writing programs for their own use, or for their colleagues who can also program in python, python provides a virtually ‘build free’ development cycle. Building can be virtually ignored.

For web servers, where millions can use the site build in python and installed just once, python also provides a close to a build free experience, and this time even for those who program as a profession.

For applications or mobile apps to be distributed for the mass market, and particularly apps/application by professionals to earn revenue, the build free experience is no longer available and building then can be complex and more difficult than with kotlin.

Python: With respect to building, python is at its best for programs written to be used by the developer and other programming colleagues, or for web servers or web services.

All kotlin programs require building. For “hello world” it can be so automatic you will not notice, but you will notice soon, even if only building programs for you own use.

However, the build systems available with kotlin are more consistent and more powerful. In fact, some notable large scale python projects like linkedin use kotlin ecosystem build systems. Despite the desirability of the kotlin approach, building in python is scattered that developers moving to kotlin can have their be the first ever exposure to build systems when using kotlin.  Note: in an ideal world, all python projects could use the same build system as other languages such as kotlin  (as is the case with linkedin).

There is a learning curve to building with kotlin, and that will be nice shallow curve if you keep it simple or a very steep curve if  try to learn the most advanced building possibilities at the start (when you don’t need them).

Kotlin: you can have one build system to build everything, including professional application, in place of the several different systems needed with python, and the power available is so compelling that advanced python users invest considerable resources to making the same system available with python.


Starting with gradle, creating a simple build.gradle

(note: proof edits pending)

Gradle a very well known building tool, originating in JVM world, but spreading to wider and wider use. A building tool is designed to automate workflows, particularly for building programs but in fact can automate any workflow iincluding building, linting, testing, pushing and much more to make your life easier. Sometimes it could be hard to grasp the concept even after reading the official documentation. This guide focuses on the basics and can enable having gradle up and running within a few minutes.

1. Gradle Introduction
2. A practical workflow for working with gradle
3. Add plugins
4. Configure the plugin
5. Add the dependencies of your project
6. Configure the project information
7. End

Continue reading “Starting with gradle, creating a simple build.gradle”