The Magic of Kotlin/Native: Part 3

Vivek Singh
AndroIDIOTS
Published in
6 min readFeb 24, 2019

Kotlin/Native Series

1. The Magic of Kotlin Native: Part 1

2. The Magic of Kotlin Native: Part 2

3. The Magic of Kotlin Native: Part 3 ( You are here)

In the last part of the Kotlin/Native series we are going to create a very simple app that has its core logic in common code but uses the native platform’s abilities to display it on the respective devices.

The application that we are going to create is a simple hello platform app. Its going to work in iOS, Android, JVM and even on a Webpage using the java script plugin.

  1. For iOS we will use the Kotlin/Native plugin
  2. For Android - kotlin-platform-android plugin
  3. For JVM - kotlin-platform-jvm plugin
  4. For JS - kotlin-platform-js plugin

The common code would use koltin-platform-common plugin.
All said and done your project structure would look something like this

At this point in time JetBrains does not have a single tooling that you can use to shuffle between the .swift files for iOS and .kt for android so we are going to be shuffling between Android studio and Xcode.

The Common Code

Before checking out the code that I’ve put in this module ask yourself this question

What can comprise common code?

Well IMO anything that falls in the following categories can be bundled together and put at a common place

  • Models
  • Networking Classes
  • Contracts
  • Business Logic
  • Validations

This list is by no means exhaustive and you can always extend it. Not only would that reduce the size of your organisation’s code base but prevent things like this :

Imagine the Product Manager’s cringe when he sees different errors on different platforms.

Now lets have a look at the files inside ExploringMultiPlatform-common module.

package demo.multiplat

expect class Platform {
val name: String
}

expect fun getPlatform(): Platform

Above is a snippet from Platform.kt. Take a close look at the expect keyword. Any module that is going to depend on this module would need to implement the classes and the methods that are marked as expect .

package demo.multiplat

fun sendGreeting(platform: Platform, name: String): String {
return "Common: Hi $name, Welcome to ${platform.name}"
}

This is a snippet from Common.kt As you can see it simply takes a Platform object and prints out its name. This corresponds to the business logic that every application has independent of its platform.

Android Implementation

Let’s create a module ExploringMultiplatform-android which will be containing our android related stuff.
This is how your build.gradle should look like:

apply plugin: 'com.android.application'
...
apply plugin: 'kotlin-platform-android'
android {
...
}

dependencies {
expectedBy project(":ExploringMultiPlatform-common")
...
}

This essentially tells the compiler that this module is expected to implement the common module.

Let’s take a look at the android implementation of Platform.kt

package demo.multiplat

actual class Platform {
actual val name: String
get() = "Android"
}

actual fun getPlatform(): Platform {
return Platform()
}

Notice the keyword actual . This keyword corresponds to the expect keyword we defined earlier in our common module.

Note: You need to have the same package name for all expect-actual counterparts otherwise it wont work.

All the heavy lifting is done at this point. You can start using the sendGreeting method from your common code by simply importing it.

iOS Implentation

So finally the moment you’ve all been waiting for. Kotlin and android have coexisted for years now. Kotlin and iOS, now that’s exciting.

Similar to android, create a gradle file ( yeah i know creating gradle files for iOS project sounds strange but stay with me a bit). Now to make things clear this gradle file will not build your actual iOS project. Instead this will create the necessary artifacts that you will need to import into your project using Xcode. And all this happens thanks to the Kotlin/Native plugin.

If you’ve gone through the Part 2 of this series you can see how to convert .kt files to an apple framework. Well thats exactly what the Kotlin/Native gradle plugin here does internally.

plugins {
id "org.jetbrains.kotlin.konan" version "1.3.0-rc-190"
}

konanArtifacts {

framework('KMulti', targets: ['iphone_sim', "iphone"]) {

// Enable multiplatform support for this artifact.
enableMultiplatform true

// Enable compiler optimizations (true/false).
enableOptimizations true

// Enable debugging for binaries generated (true/false).
enableDebug true

// Print all parameters during the build.
dumpParameters false

// Print time of compilation phases (equivalent of the `--time` command line option).
measureTime false
}
}

dependencies {
expectedBy project(":ExploringMultiPlatform-common")
}

Similar to android’s gradle we are expecting this module to implement our common project.
The konanArtifacts block creates the actual framework depending upon the given targets.

package demo.multiplat

import platform.UIKit.UIDevice

actual class Platform {
actual val name: String
get() =UIDevice.currentDevice.systemName()
}

actual fun getPlatform(): Platform {
return Platform()
}

As you can see I can even import UIKit and other iOS(macOS) libraries.

Running this module will create KMulti.framework files for all the targets mentioned in the build.gradle.
Remember I told you there isnt great tooling for simultaneous editing of swift and kotlin files. Just open up Xcode and import the generated framework file and you are good to go.

Your ViewController will look like this when using the KMulti library

import UIKit
import KMulti

class ViewController: UIViewController {

@IBOutlet weak var label: UILabel!
@IBAction func clicked(_ sender: Any) {
let greeting = CommonKt.sendGreeting(platform: PlatformKt.getPlatform(), name: tf.text ?? "No Name")
label.text = greeting
}

@IBOutlet weak var tf: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.

label.text = ""
}


}

Your final results should look something like this

Android and iOS

--

--