Kotlin Multiplatform Mobile : IOS and Android Apps with common native code
In Kotlin Native, the Kotlin code is complied into native binaries and run without any virtual machine. The Kotlin/Native has different compilers for various operating systems like macOS, Windows, and Linux.
Kotlin/Native compiler converts the Kotlin code to native binaries, therefore no virtual machine is required.
Cross platform with Kotlin/Native (Android and IOS Apps)
Using Kotlin/Native we can use Kotlin code in Swift/Objective-C and vice versa. We can create a framework for macOS and iOS from Kotlin code using Kotlin/Native compiler. This framework contains all declarations and binaries that are required by Objective-C and Swift. Kotlin/Native doesn’t use common UI , it focuses on common Business logic.
Flutter / React Native and Kotlin Native
The main difference between Flutter / React Native and Kotlin Native is, it provides common code for almost every thing except UI. We have to create UI of Android using native android code (XML based) and IOS using SwiftUI or other native techniques.
Creating a Kotlin/Native Project
Lets start with creating a simple Android project in Android Studio. While creating the Android project select the language Kotlin and name of the Application KotlinNativeExample.
Creating the Shared Module
Now we have to create a shared module, which will be used by both Android and IOS Apps. In our project directory let us now create a directory shared (Any name you can give)
Create shared/src/commonMain/kotlin
directory. Now create common.kt
file in commonMain
directory. also create androidMain
and iosMain
directories for android and IOS specific code
in common.kt let us define a function which we will invoke from both platforms
package com.kotlinnative.shared
expect fun getPlatformName(): String
class HelloWorld {
fun sayWelcome(): String = "Welcome, ${getPlatformName()}"
}
expect function is the signature of the platform specific function, which will be implemented separately using Android and IOS specific code.
Android specific code is in androidMain/kotlin folder (android.kt file)
package com.kotlinnative.shared
import android.os.Build
actual fun getPlatformName(): String {
return "Android ${Build.VERSION.RELEASE}"
}
Similarly the IOS specific code is in iosMain/kotlin folder (ios.kt file)
package com.kotlinnative.shared
import platform.UIKit.UIDevice
actual fun getPlatformName(): String {
return "${UIDevice.currentDevice.systemName()}
${UIDevice.currentDevice.systemVersion()}"
}
We have to add build script in shared module. create a build.gradle file in shared folder. This is a common script or boilerplate code to create libraries for different OS (Android and IOS)
apply plugin: 'com.android.library'
apply plugin: 'kotlin-multiplatform'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 15
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
kotlin {
targets {
final def iOSTarget = System.getenv('SDK_NAME')?.startsWith('iphoneos') ? presets.iosArm64 : presets.iosX64
fromPreset(iOSTarget, 'ios'){
binaries {
framework('shared')
}
}
fromPreset(presets.android, 'android')
}
sourceSets {
// for common code
commonMain.dependencies {
api 'org.jetbrains.kotlin:kotlin-stdlib-common'
}
androidMain.dependencies {
api 'org.jetbrains.kotlin:kotlin-stdlib'
}
iosMain.dependencies {
}
}
}
configurations {
compileClasspath
}
task packForXCode(type: Sync) {
final File frameworkDir = new File(buildDir, "xcode-frameworks")
final String mode = project.findProperty("XCODE_CONFIGURATION")?.toUpperCase() ?: 'DEBUG'
final def framework = kotlin.targets.ios.binaries.getFramework("shared", mode)
inputs.property "mode", mode
dependsOn framework.linkTask
from { framework.outputFile.parentFile }
into frameworkDir
doLast {
new File(frameworkDir, 'gradlew').with {
text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$@\n"
setExecutable(true)
}
}
}
tasks.build.dependsOn packForXCode
Create AndroidManifest.xml in shared/main folder
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kotlinnative.shared" />
now Sync and Build shared module
Creating Android Build
In order to use this common module in Android we have to add this module in settings.gradle
include ':app', ':shared'
rootProject.name='KotlinNativeExample'
Add the shared module and other packagingOptions in build.gradle of Android project
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android { ... packagingOptions {
exclude 'META-INF/*.kotlin_module'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':shared')
...
}
Now invoke the function from shared module in Android App
package com.kotlinnative.example
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.kotlinnative.shared.HelloWorld
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
displayText.text = HelloWorld().sayWelcome()
}
}
Sync , Build and Run the code on Android device.
IOS Project
Create a new folder inside the KotlinNativeExample named iosApp and in that folder create a new iOS Project (use Swift language).
Now go to the Android studio and sync the shared module. Open the terminal, and execute the following command
./gradlew :shared:build
after build is completed you can see the build folder of shared module
Now open Xcode and goto the build phase, add a new run script using + icon on top.
add following script
cd "$SRCROOT/../../shared/build/xcode-frameworks"
./gradlew :shared:build -PXCODE_CONFIGURATION=${CONFIGURATION}
build phases must be in correct order: Target Dependencies and below that Run Script
In Build Settings search for Enable Bitcode and set its value to No
Now Build Settings search Framework Search Paths , add following path
$(SRCROOT)/../../shared/build/xcode-frameworks
Now add the framework , in general tab click on the plus icon on Embedded Binaries, select Add Other and select the framework from ../shared/build/xcode-frameworks
All the setting are done, we have to import shared in our code and use Kotlin Native function in it
Clean , Build and run on IOS device
Wow !!! we have developed our first Kotlin native application, running on Android and iPhone with common code.
You can download the source code from