Originally written by Aman Bansal

Create Hello World App with KMM 📱- Android & IOS

Translator: Bingxin said

In mobile development, Android and iOS versions of apps often have a lot in common, and the business logic behind them is basically the same. Download files, read and write databases, get data from remote servers, parse remote data, and so on. So why don’t we just write the business logic code once and share it across different platforms?

With this in mind, Jetbrains came up with the Kotlin Multiplatform Project.

➡️ What is Kotlin Multiplatform Mobile?

Kotlin Multiplatform Mobile (KMM) is a cross-platform Mobile development SDK provided by Jetbrains. With Kotlin’s cross-platform capabilities, you can compile for multiple platforms using a single project.

With KMM, you have flexibility while retaining the benefits of native programming. Use a single code base for business logic code for Android/iOS applications and write platform-specific code only when needed, such as implementing native UI, using platform-specific apis, and so on.

KMM can be seamlessly integrated into your project. Shared code, written in Kotlin, compiled into JVM bytecode using Kotlin/JVM, and compiled into binary using Kotlin/Native, so you can use the KMM business logic module just as you would any other generic class library.

At the time of writing this blog, KMM is still in Alpha and you can start experimenting with sharing business logic code in your applications.

KMM is currently unknown to the general public in mobile development. Jetbrains has developed the KMM plugin for Android Studio to help you quickly set up your KMM project. Plug-ins can also help you write, run, and test shared code.

➡️ Build the HELLO WORLD KMM application step by step

  1. Install the Kotlin Multiplatform Mobile plug-in on Android Studio. Open Android Studio -> click Configure -> select Plugins

  1. Select Marketplace in the Plugins section, search for KMM, install and restart Android Studio.

  1. On the Android Studio homepage, select “Start a New Android Studio Project”.

  1. On the Select a Project Template page, Select KMM Application.

  1. Set the project name, minimum SDK, file directory, package name, etc.

Now, you need to wait for the first build of the project, which takes some time to download and set up the necessary components.

Note: The KMM plugin requires you to have at least version 4.0 of the Kotlin plugin

➡️ Run your program

Select the platform you want to Run on from the menu bar, select the device, and click Run

To run iOS apps, you need to install Xcode and emulator.

➡️ Take a look at the code

Android developer? Look familiar? 😎

IOS developer? Looks like an alien? 👽

➡ ️ module

  • Shared module — The Kotlin module, which houses the Common Android/iOS business logic code, is compiled into the Android Library and iOS framework. Build with Gradle.

  • AndroidApp module — The Kotlin module for Android apps. Build with Gradle.

  • IosApp module — Xcode project for building iOS apps.

Project build.gradle. KTS file:

buildscript {
    repositories {
        gradlePluginPortal()
        jcenter()
        google()
        mavenCentral()
    }
    dependencies {
        classpath("Org. Jetbrains. Kotlin: kotlin - gradle - plugin: 1.4.10")
        classpath("Com. Android. Tools. Build: gradle: 4.0.1." ")
    }
}
group = "com.aman.helloworldkmm"
version = "1.0 the SNAPSHOT"

repositories {
    mavenCentral()
}
Copy the code

➡ ️ Shared module

The shared module contains common code for Android and iOS. However, in order to implement the same logic on Android/iOS, sometimes you have to write two versions of version specific code, such as Bluetooth, Wifi, etc. To handle this, Kotlin provides an Expect /actual mechanism. The source code for the shared module is sorted into three source sets:

  • commonMainStores code that works for all platforms, includingexpectThe statement
  • androidMainStores Android specific code, includingactualimplementation
  • iosMainStore iOS specific code, includingactualimplementation

Each source set has its own dependencies. The Kotlin library dependencies are automatically added to all source sets and you do not need to declare them in compiled scripts.

build.gradle.kts

The build.gradle. KTS file contains the shared module’s configuration for Android/iOS.

import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget

plugins {
    kotlin("multiplatform")
    id("com.android.library")
    id("kotlin-android-extensions")
}
group = "com.aman.helloworldkmm"
version = "1.0 the SNAPSHOT"

repositories {
    gradlePluginPortal()
    google()
    jcenter()
    mavenCentral()
}
kotlin {
    android()
    ios {
        binaries {
            framework {
                baseName = "shared"
            }
        }
    }
    sourceSets {
        val commonMain by getting
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))}}val androidMain by getting {
            dependencies {
                implementation("Com. Google. Android. Material: material: 1.2.0")}}val androidTest by getting {
            dependencies {
                implementation(kotlin("test-junit"))
                implementation("Junit: junit: 4.12")}}val iosMain by getting
        val iosTest by getting
    }
}
android {
    compileSdkVersion(29)
    sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
    defaultConfig {
        minSdkVersion(24)
        targetSdkVersion(29)
        versionCode = 1
        versionName = "1.0"
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false}}}val packForXcode by tasks.creating(Sync::class) {
    group = "build"
    val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
    val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator"
    val targetName = "ios" + if (sdkName.startsWith("iphoneos")) "Arm64" else "X64"
    val framework = kotlin.targets.getByName<KotlinNativeTarget>(targetName).binaries.getFramework(mode)
    inputs.property("mode", mode)
    dependsOn(framework.linkTask)
    val targetDir = File(buildDir, "xcode-frameworks")
    from({ framework.outputDirectory })
    into(targetDir)
}
tasks.getByName("build").dependsOn(packForXcode)
Copy the code

AndroidApp module build.gradle. KTS file

plugins {
    id("com.android.application")
    kotlin("android")
    id("kotlin-android-extensions")
}
group = "com.aman.helloworldkmm"
version = "1.0 the SNAPSHOT"

repositories {
    gradlePluginPortal()
    google()
    jcenter()
    mavenCentral()
}
dependencies {
    implementation(project(":shared"))
    implementation("Com. Google. Android. Material: material: 1.2.0")
    implementation("Androidx. Appcompat: appcompat: 1.2.0")
    implementation("Androidx. Constraintlayout: constraintlayout: 1.1.3.")
}
android {
    compileSdkVersion(29)
    defaultConfig {
        applicationId = "com.aman.helloworldkmm.androidApp"
        minSdkVersion(24)
        targetSdkVersion(29)
        versionCode = 1
        versionName = "1.0"
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false}}}Copy the code

➡️ use the Expect/Actual keyword

Version-specific code is common for cross-platform applications. For example, you might want to know if your app is running on an Android or iOS device and get the device model. To do this, you need to use the Expect /actual keyword.

First, declare an empty class or function in the Common module using the Expect keyword, just as you would create an interface or abstract class. Platform-specific code is then written in all the other modules to implement the corresponding classes or functions, decorated with actual.

Note that if you use Expect, you must provide an actual implementation for the name.

Otherwise, you will get the following error:

➡️ use of Expect/Actual

commonMain

expect class Platform() {
    val platform: String
}
Copy the code

androidMain

actual class Platform actual constructor() {
    actual val platform: String = "Android ${android.os.Build.VERSION.SDK_INT}"
}
Copy the code

iosMain

import platform.UIKit.UIDevice


actual class Platform {
    actual val platform: String = UIDevice.currentDevice.systemName() + "" + UIDevice.currentDevice.systemVersion
}
Copy the code

MainActivity.kt (Android)

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val tv: TextView = findViewById(R.id.text_view)
        tv.text = "Hello World, ${Platform().platform}!"}}Copy the code

ContentView.swift (iOS)

struct ContentView: View {
    var body: some View {
        Text("Hello World, "+ Platform().platform)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Copy the code

Congratulations!!!!! You have completed your first KMM app.

➡️ Open source KMM application

  • JetBrains/kotlinconf-app
  • saket/press
  • jarroyoesp/KotlinMultiPlatform

➡️ Available KMM class libraries

AAkira/Kotlin-Multiplatform-Libraries

The translator said

Kotlin’s approach to cross-platform mobile development, which is already a sea of success, allows you to continue to use the platform’s native approach to UI development and “Write once, run everywhere” business logic. Maybe even give yourself a break. Could Flutter one day be used for UIS and Kotlin for business logic?

At the end of the day, developers have to buy it. Don’t know what you think of KMM, leave your thoughts in the comments!

Finally, I would like to advertise a wave of my small column, Android review notes for interviews, so far I have output six articles, interested can give a subscription.

Android review Notes directory

  1. Chatter task stack, back stack and start mode
  2. The life cycle of chatter activities
  3. Grilled steak a Context
  4. Why not display Dialog using Application Context?
  5. Can OOM be tried catch?
  6. OnDestroy () 10 seconds after activity.Finish ()?