Photo by Dan Gold on Unsplash

Multiplatform Programming Using Kotlin Native — a mobile developer’s quest! — Part 1

Arjun Kalidas
Dev Machina
Published in
10 min readJun 21, 2019

--

Kotlin Multiplatform, some of you might have heard this being thrown around at your work, or if you are a mobile app developer keen on following the latest news in the app development world. Kotlin, as a language is very mature now and gradually many companies and individuals are moving towards rewriting or implementing new modules in Kotlin. We have been seeing multiplatform app development from last few years using Cordova, ionic and ReactNative. Although, that required developers to learn a new language (JavaScript, Typescript etc…) altogether and leave behind their favorite native languages such as Java and Swift/Objective-C. But what Kotlin does to multi-platform is a good compromise between pure hybrid and native, when they let you keep your native UI logic and other platform specific codebase and still incorporate Kotlin to abstract out business logic that can be made interoperable between the platforms. Kotlin can be used as the language that bridges native code of various platforms. It can work seamlessly for web and mobile technologies. Here we are going to discuss about the two major mobile platforms — Android and iOS. Without further ado, let’s jump into setting up a Kotlin Multiplatform (also referred as Kotlin-MPP) project.

For simplicity, we will be splitting the post into three parts:

Introducing Kotlin Multiplatform and groundwork

Building Kotlin Multiplatform App — Part 2

Troubleshooting guide for a beginner — Part 3

It is a fairly straightforward procedure to setup the project structure for a Multiplatform project as per the Kotlin MPP website. But unfortunately, I have come to experience differently. I ran into a lot of issues and there are still a lot of unknowns when it comes to programming in Kotlin/Native for iOS and building framework to support Xcode development.

I am laying out some of the challenges I faced on my journey to make a “Proof of Concept” Multiplatform project. My intention behind this document is to disseminate the knowledge I gained and piece together an elaborate setup & troubleshooting guide.

We can go the “IntelliJ” way or “Android Studio” way. I will be explaining the Android Studio way for the sake of simplicity. When we create a similar project in “IntelliJ”, there is an overhead of adding a lot of files and gradle wrapper itself in the project level hierarchy. For building an app on Android, Windows Operating System is sufficient whereas for iOS applications, a Mac installed with Xcode is required.

Our key takeaways from this tutorial will be:

We’ll learn how to:

  • Create an Android app with Android Studio
  • Create a shared Kotlin library
  • Use it from Android app
  • Start the Android application
  • Create an iOS app with Xcode
  • Use the shared Kotlin library from iOS app
  • Use Kotlin from Swift
  • Start the iOS application

Prerequisites:

Install JDK — preferably the latest available on the website. Please follow the instructions on the website according to your OS (Windows/Mac)

https://www.oracle.com/technetwork/java/javase/downloads/index.html

Install Android Studio — follow the instructions on the website according to your OS (Windows/Mac)

https://developer.android.com/studio/index.html

If you are on a Mac, you can go ahead and install a package manager like Brew.

https://brew.sh

Install Gradle globally using the package manager

https://gradle.org/install/

Install Xcode for Mac users for building the app for iOS platform

Note: I am using Android Studio 3.4, Kotlin 1.3.21, Xcode 10.0, macOS 10.14, Gradle 5.1.1

After you are done satisfying the prerequisites, you are all set! Let’s take a deep dive.

Our goal with this tutorial is to gain an understanding of using Kotlin for sharing code between two platforms namely, Android and iOS. I will be demonstrating a simple application that has common code written in Kotlin and shared between the two platforms.

The application I am demonstrating will print few basic Strings like “Hello from Android” and “Hello from iOS” based on the platform. Although this same pattern can be extended to build a huge project. Essentially what I mean is size doesn’t matter, it will work for all kinds of projects.

Setting up an Android Project

Let’s create a new Android project from the launch wizard.

Android Studio launch window

Select “Start a new Android Studio Project”, and you will see the wizard below

Project Selection Wizard

Select “Empty Activity” for our sample app, and you will be redirected to a menu seen like below

Project Configuration window

Also, an important point to note here is that that “Language” selected is “Kotlin”. And continue with default settings for now and proceed.

Hit “Finish” and you will be redirected to your project like below

Project structure tab

We can use a stable version of Kotlin maven plugin or an EAP (Early Access Preview) version. We have to reference the version of Kotlin we are using by adding the maven URL in the Gradle file.

The maven URL as shown below should be added in the two repositories tag “{..}” in the main build.gradle file.

maven { url ‘https://dl.bintray.com/kotlin/kotlin-eap' }
Highlighting the Gradle file

Note the file marked with a red oval, this is where you will update the maven URL.

build.gradle file in the project level

This is how your updated file will look like.

Additionally, Kotlin/Native will require the latest version of Gradle. So, to patch the Gradle version if it’s not already latest, let’s open the gradle-wrapper.properties file residing under “gradle → wrapper” folder hierarchy.

Kotlin Multiplatform projects require Gradle version 4.7 and above, older Gradle versions are not supported.

Add the below line to this file:

distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
Highlighting the gradle wrapper

We have to Sync Gradle to ensure that all these changes we made to the project are applied successfully. Click on “Sync now” in the Android Studio or alternatively open the terminal in the Android Studio and run the below command

./gradlew build

Now, to make sure everything is working fine, run the application as an android application.

You must have downloaded at least one SDK to launch the Android emulator of a desired hardware configuration

When you try to run the target, you will be prompted with a menu like below:

Android AVD manager to launch an emulator

If you have not configured an SDK and downloaded a simulator, it will be blank for you.

Now let’s head to the exciting part we have been waiting for, the “Shared Module” in Kotlin for enabling Multiplatform support.

Create Shared Module

The goal of this tutorial is to demonstrate the code sharing ability of Kotlin and re-usability across Android and iOS platforms. Let’s create a “SharedModule” sub-project in our Gradle project. The code written in the “Shared Module” will be shared between the platforms. And to get there, we have to follow some steps while adding new files and folders in a well defined structure.

  1. Right-click on the “My App” project title in the “Project Navigator”
Creating a sub-module in an Android Project

2. This is where things get a bit messy. None of the bloggers or the official Multiplatform documentation mentions what kind of sub-project you should be creating. I had a tough time figuring out. So I am laying down those steps.

3. Go for “Java Library”. The reason why you are choosing Java Library is because it gives you a well-defined structure that you will see in a bit.

Configure Java Library module

4. Enter your “Package Name” that you wish to add. And rename the “Library Name” to “SharedModule”. You can essentially have any name, but I have chosen this name for our tutorial’s sake. Hit “Finish” and wait for the module to be created.

5. Create three folder hierarchies under the “SharedModule → src”

SharedModule → src → commonMain → kotlin → common.ktSharedModule → src → androidMain → kotlin → actual.ktSharedModule → src → iosMain → kotlin → actual.kt

After creating all the folders and files, it will look like below. You can also note that I have left “main” folder and the hierarchy below untouched. It’s not necessary. We can delete the same. But before doing so, if you don’t remember the package name, just go to “MyClass” and copy the first line that says “package…” and paste it in the three Kotlin files you created just now.

SharedModule structure
Mentioning package name in the newly created Kotlin files

Let’s update the main file under SharedCode/src/commonMain/kotlin/common.kt

package com.example.sharedmoduleexpect fun platformName(): Stringfun createApplicationScreenMessage() : String {return “Hello World, from ${platformName()}”}

This is the file where common code remains. The code to generate the final message. It expects the platform to provide the platform name from the expect fun platformName(): String function. We will use the createApplicationMessagefrom both Android and iOS applications.

Now, let’s create the implementation for Android in the SharedCode/src/androidMain/kotlin/actual.kt:

package com.example.sharedmoduleactual fun platformName(): String {return “Android”}

Now we will create a similar file for the iOS target in the SharedCode/src/iosMain/kotlin/actual.kt:

package com.example.sharedmoduleimport platform.UIKit.UIDeviceactual fun platformName(): String {return UIDevice.currentDevice.systemName() + “ “ +UIDevice.currentDevice.systemVersion}

You might be surprised to see UIDevice class from Apple UIKit framework. This is not available from Java libraries, instead it is supported only in Objective-C and Swift. And Kotlin/Native compiler is pre-loaded with a set of pre-imported frameworks, so we can use the UIKit framework without any additional configuration. To know more about Swift and Objective-C Interop with Kotlin/Native please visit the below link:

https://kotlinlang.org/docs/reference/native/objc_interop.html

Now, we have a defined project structure that has an Android “app” module and a “Java” library by name “SharedModule”. But we are not done yet. We have to patch some more files and configured to download certain artifacts from the Kotlin maven repository.

One of the key capabilities of Kotlin’s multi platform code is a way for common code to depend on platform-specific declarations. In other languages, this can often be accomplished by building a set of interfaces in the common code and implementing these interfaces in platform-specific modules. However, this approach is not ideal in cases when you have a library on one of the platforms that implements the functionality you need, and you’d like to use the API of this library directly without extra wrappers. Also, it requires common declarations to be expressed as interfaces, which doesn’t cover all possible cases.

As an alternative, Kotlin provides a mechanism of expected and actual declarations. With this mechanism, a common module can define expected declarations, and a platform module can provide actual declarations corresponding to the expected ones.

Patching the Gradle Scripts

The “SharedModule” sub-project has to generate certain modules for us to continue:

  • JAR file for Android project, from the androidMain source set
  • Apple framework
  • for iOS device and App Store (arm64 target)
  • for iOS emulator (x86_64 target)

Let’s update the Gradle Scripts. We have to be very careful while editing in the steps below. Let’s go through them in detail and understand what each line of the script means!

First, let’s add the below line in the “settings.gradle” file. And perform “Sync Now” operation.

include ‘:SharedModule’

This means that we are including the ‘SharedModule’ as another qualified target to the whole project.

Including the SharedModule as a dependency in the settings.gradle file

After adding SharedModule to the settings, we have to make changes to the “build.gradle” file of the SharedModule sub-project.

apply plugin: ‘kotlin-multiplatform’kotlin {targets {final def iOSTarget =        System.getenv(‘SDK_NAME’)?.startsWith(“iphoneos”) \ ? presets.iosArm64 : presets.iosX64fromPreset(iOSTarget, ‘ios’) {binaries {framework(‘SharedModule’)}}fromPreset(presets.jvm, ‘android’)}sourceSets {commonMain.dependencies {api ‘org.jetbrains.kotlin:kotlin-stdlib-common’}androidMain.dependencies {api ‘org.jetbrains.kotlin:kotlin-stdlib’}}}configurations {compileClasspath}

The first line that says “apply plugin: ‘kotlin-multiplatform’ “ applies Multiplatform features to the project. This plugin essentially adds all the artifacts required for us to use.

The “common” target contains the common code written in Kotlin which is compiled and included into every platform. It will contain “expect” declarations and other targets provide “actual” implementations according to the platform.

  • kotlin“kotlin target”,defining targets for iOS and Android (An iOS target for both the iPhone and Simulator is defined in the variable “iOSTarget”.)
  • presets — if it is an iOS device, create a binary, whose name is “SharedModule”, and if it is an Android device, create it from “presets.jvm”
  • sourceSets — defining the dependencies for each platform, here android and common module
  • In future if there is an iOS dependency, add another clause “iosMain.dependencies {… } and add the corresponding library
  • configurations — there are different configurations, here we are asking Gradle to compile this library (https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_configurations_graph) which is the subfolder name in the hierarchy “src → commonMain → kotlin”

Initially, your “SharedModule” build.gradle might be either blank or might contain something like below:

And finally this how we will have the artifacts.

The table describing the name of sourceset and the artifact generated

Just paste the snippet above into this Gradle file and hit “Sync Now”. Now, if you see the folder structure, the SharedModule would have been recognized by the IDE. You can notice the “tea cup” icon on all three “kotlin” folders.

SharedModule with sourcesets configured

In the second part, we will discuss how we can configure for Android and iOS! So head over.

--

--

Arjun Kalidas
Dev Machina

I write about common software engineering problems & solutions to them, app development, machine learning, AI and blockchain! Hit me up on Twitter/LinkedIn.