App Development

A Quick Start Guide to Kotlin Multiplatform

Quickstart-Guide-to-Kotlin-Multiplatform

Kotlin Multiplatform, though still experimental, is a great up-and-coming solution for sharing code between several platforms. Using Kotlin Multiplatform, it is possible to write the entirety of an app outside of platform UI, in one location. Let’s take a look at setting up a very basic Hello World app for Android and iOS.

Start by creating a new project in Android studio using Kotlin. There’s no need to add an Activity at this time; you can select an Empty Activity.

Create the Shared Project

The first thing we need to do is to create a module that will be shared among all platforms. The Android app will reference this module directly, while other platforms will reference a packaged version of this module. The module will be split up into several source sets based on the platform targets we define. By default, each target is given a source set both for the main code and for tests, though it is possible to create additional sets.

In Android Studio, add a new “Android Library” module. Then open up the build.gradle file to configure the multi-platform behavior. We will need to add the ‘kotlin-multiplatform’ plugin, platform targets, dependencies for each target, and finally, code to generate an Xcode framework for the iOS project to reference.

apply plugin: 'kotlin-multiplatform'

kotlin {
    targets {

        // Android Target
        android()

        // Select iOS target for real device or emulator.
        final def iOSIsRealDevice = System.getenv('SDK_NAME')?.startsWith("iphoneos")
        final def iOSTarget = iOSIsRealDevice ? presets.iosArm64 : presets.iosX64

        // iOS target.
        fromPreset(iOSTarget, 'ios') {
            binaries { framework('shared') }
        }
    }
}

dependencies {
    def kotlin_stdlib = "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    commonMainImplementation kotlin_stdlib
    androidMainImplementation kotlin_stdlib
    iosMainImplementation kotlin_stdlib
}

// Task to generate iOS framework for xcode projects.
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)
        }
    }
}

// Run packForXCode when building.
tasks.build.dependsOn packForXCode

Once you sync your gradle files, you will see .iml files created for the default source sets. Note that there are not .iml files for android, though.

kotlinblog1.1

Create the Shared Code

Though the source sets have been configured, we will still need to create the directories in the format “[platform]Main/kotlin”. Under “shared/src” add these directories: “commonMain/kotlin”, “androidMain/kotlin”, “iosMain/kotlin”.

In the common code, we will create two functions. One will be a common function to be called from within each target platform’s code that returns a message. The other function defines a function that must be implemented by each platform’s Main source set and will return a name for that platform. Notice the expect before getPlatformName. This keyword is a part of the mechanism that gives us multi-platform functionality from a common code base. The expect keyword marks something (function, class, variable, etc.) as platform-specific, and which must be implemented in each platform code using the actual keyword. To help enforce this rule, the compiler will throw an error if expect’s and actual’s do not match up.


fun helloWorld(): String = "Hello, ${getPlatformName()}!"

expect fun getPlatformName(): String```


Under the “androidMain/kotlin” directory, create a file that will implement common’s expected function.

```package com.sharedpackage.name

import android.os.Build

actual fun getPlatformName(): String = "Android ${Build.VERSION.RELEASE}"```


And finally, repeat the above for iOS.

`package com.sharedpackage.name`

```private val systemName = UIDevice.currentDevice.systemName
private val systemVersion = UIDevice.currentDevice.systemVersion
private val name = UIDevice.currentDevice.name

actual fun getPlatformName(): String = "$systemName $systemVersion $name"```


Now we can start building very basic apps to utilize this shared code.

### Create the Android App

For the Android app, just add an Empty Activity and reuse the TextView provided to display the HelloWorld message. In the `onCreate` function, we can call the common function `helloWorld` and set the result as the text of our TextView.

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

    platform_label.text = helloWorld()
}

Run the app and it should look something like this:

Kotlinblog2.1

Create the iOS app

Setting up the iOS project is a bit more involved than the Android app, but it’s pretty easy to follow once you see it. The iOS app could technically exist outside of the project we created in AndroidStudio, but it is a good idea to keep everything contained in one place, so create a directory in the root project in which to create the iOS project. Then we need to build the Xcode framework by running ./gradlew clean build from the terminal in the main project directory. In Android Studio, you can also open the “Gradle” tab and run the :shared -> Tasks -> build -> build task instead of using the terminal.

While that’s running, we can open Xcode and create a “Single View App” project in our iOS directory. Then we will need to setup some build steps and import the generated Xcode framework.

With the .xcodeproj file selected, select the “Build Settings” tab and locate “Framework Search Paths”. Here we will add the gradle build directory where the framework is generated: “$(SRCROOT)/…/…/shared/build/xcode-frameworks/”. Now would be a good time to make sure that our gradle build has completed and that we can find “shared.framwork” in the expected directory.

Next select the “Build Phases” tab. Add a Run Script Phase and place it in front of the “Compile Sources” phase. This script will simply rebuild the xcode framework to pick up any changes.

./gradlew :shared:packForXCode -PXCODE_CONFIGURATION=${CONFIGURATION}

Under “Embed Frameworks”, we will need to add a reference to “shared.framework”. Make sure “Copy items if needed” is not checked and select “Create folder references”.

With the framework in place, we can add a UILabel via the storyboard and then set the text in code. Import the shared framework and set the text by calling the helloWorld function.

import shared

class ViewController: UIViewController {
    
    @IBOutlet weak var platformLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        platformLabel.text = PlatformKt.helloWorld()
    }
}

Run the app and it should look something like this:

Kotlinblog3.2

Conclusion

While setting up a Kotlin Multiplatform project can be a bit involved, it’s quite manageable once you get the hang of it. This was a very basic example using an Android and iOS app, but there is a lot more information and many more examples in the official documentation. In addition to Android and iOS, you can add platform targets for JavaScript, Mac, Linux, and Windows, and you can create additional source sets for each target beyond the default “Main” and “Test”. There are also gradle plugins for common and platform modules that allow you to create libraries for use in other projects.

While Multiplatform libraries are still uncommon, there are several options that are equipped for the basic needs of an app, like networking, serializing, and dependency injection! A running list can be found here.

Kotlin Multiplatform is certainly not complete, but it is in a great place to start building multi-platform apps.

Join our team to work with Fortune 500 companies in solving real-world product strategy, design, and technical problems.

Find Your Role
Five-Things-to-Know-About-The-iOS-Speech-Framework

Four Things To Know About the iOS Speech Framework

Voice experiences have become more common through interactions with assistants like...

Read the article