Chapters

Hide chapters

Kotlin Apprentice

Third Edition · Android 11 · Kotlin 1.4 · IntelliJ IDEA 2020.3

Before You Begin

Section 0: 4 chapters
Show chapters Hide chapters

Section III: Building Your Own Types

Section 3: 8 chapters
Show chapters Hide chapters

Section IV: Intermediate Topics

Section 4: 9 chapters
Show chapters Hide chapters

26. Kotlin Multiplatform
Written by Victoria Gonda

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

As of 2019, Kotlin is the preferred language to use for Android development. On iOS, the Swift language has replaced Objective-C as the de facto development language.

Swift and Kotlin have a multitude of similarities, including static typing, type safety, support for the functional and OOP paradigms, and safe-handling of null values. The languages are also syntactically very similar.

The advent of Kotlin/Native has opened up the possibility of integrating Kotlin code into your iOS projects. In fact, the Kotlin Multiplatform (KMP) approach is beginning to take off as a cross-platform toolset for iOS, Android, and beyond.

In this chapter, you’ll use Android Studio and Xcode to create a KMP project.

Note: Because KMP can also be used with JavaScript, in documentation you might also see reference to KMM which is Kotlin Multiplatform Mobile, KPM specifically used for Android and iOS.

The KMP Approach

Typical apps on iOS and Android pull down data from the Internet, parse the data into objects within the app code, and cache some of the network data locally in a database.

For iOS, you might use a library like Alamofire for networking and something like JSONSerialization to parse the data received from the network into objects. You’d use Core Data to store the data locally in a database.

You’d implement an architecture pattern like MVVM to structure your app code. You might use a library like RxSwift to make your code more declarative. And you’d have tests for the view models and other parts of the app. You’d show lists of data in a UITableView in a UIViewController.

On Android, you’d have analogs of all of that code. You might use Retrofit for networking, Gson or Moshi for parsing the JSON, Room for storing the data in a database, a pattern like MVP or MVVM for the architecture, and maybe use RxJava in the app. You’d repeat similar tests. And you’d show list data in a RecyclerView in an Activity or Fragment using an adapter.

That’s a lot of duplication even for a simple app. Imagine that there were numerous screens in your app, with more data, networking calls, and local caching of the remote data, as there would be in a full-featured app. The amount of code duplication would grow essentially linearly with the size of the app, as would the amount of time and effort to first produce and then maintain the two apps for the two platforms.

Other Cross-Platform Frameworks

Reducing this duplication in targeting both iOS and Android has long been a vision of many developers and organizations in the mobile world. Early attempts included web frameworks such as PhoneGap.

Sharing Code

With Kotlin Multiplatform, you reduce code duplication by putting code common to all front-end apps into one module or shared project. This includes business logic, and things like networking code, data parsing, data persistence, and more.

HelloKMP

You’re going to build a simple app named HelloKMP that shares Kotlin code between iOS and Android apps. You’ll start in Android Studio and first setup the Android app project and the project level build files for the entire KMP project.

Renaming the app folder

The name of the android app folder is ideally something like androidApp instead of just the default app, in order to distinguish that part of the project as being the Android app.

Shared project

You’re going to build up the shared project more or less by hand. That way, you’ll see all that goes into creating the shared project.

mkdir -p shared/src/androidMain/kotlin
mkdir -p shared/src/commonMain/kotlin
mkdir -p shared/src/iosMain/kotlin
touch shared/src/commonMain/kotlin/common.kt
touch shared/src/androidMain/kotlin/android.kt
touch shared/src/iosMain/kotlin/ios.kt
touch shared/build.gradle.kts

Configuring your environment

To work with Kotlin Multiplatform projects in Android Studio, you need to download the Kotlin Multiplatform Mobile plugin. Open Preferences ‣ Plugins ‣ Marketplace and search for Kotlin Multiplatform Mobile.

Shared code build file

The shared code will be turned into the a jar file for running with the Android app, and an iOS framework for running with the iOS app. The shared project build.gradle.kts file is a Kotlin file where you will specify how that is done.

include ':shared'
plugins {
  kotlin("multiplatform")
  id("com.android.library")
}
kotlin {

}
android()
ios {
  binaries {
    framework {
      baseName = "shared"
    }
  }
}
sourceSets {
  val commonMain by getting
  val androidMain by getting {
    dependencies {
      implementation("com.google.android.material:material:1.2.1")
    }
  }
  val iosMain by getting
}

expect and actual

Compiling the entire shared project for all platforms is not the approach taken by Kotlin Multiplatform. Instead, a certain amount of code is common to all platforms, but some amount of the shared code is unique to each platform. Kotlin includes the expect/actual mechanism to allow for this.

package com.raywenderlich

expect fun platformName(): String
class Greeting {
  fun greeting(): String = "Hello, ${platformName()}"
}
package com.raywenderlich

actual fun platformName(): String {
  return "Android"
}
package com.raywenderlich

import platform.UIKit.UIDevice

actual fun platformName(): String {
  return "${UIDevice.currentDevice.systemName()}"
}

Building your projects

There are two small things you need to do before building:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.raywenderlich">

</manifest>
android {
  compileSdkVersion(29)
  sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
  defaultConfig {
    minSdkVersion(21)
    targetSdkVersion(29)
  }
}

Shared code from Android

Now it’s time to use the shared library from the Android app.

packagingOptions {
  exclude 'META-INF/*.kotlin_module'
}
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':shared')
android:id="@+id/greeting"
findViewById<TextView>(R.id.greeting).text =
  Greeting().greeting()
import android.widget.TextView
import com.raywenderlich.Greeting

The iOS app

Having used the shared project in an Android app, you now turn to using the shared code in an iOS app. But first you need to setup the iOS app project itself.

mkdir iosApp

Packing the iOS framework

Next, back in Android Studio, you need to add a task to the Gradle build file shared/build.gradle.kts for the shared project that will package the framework for Xcode.

val packForXcode by tasks.creating(Sync::class) {
}
group = "build"
/// selecting the right configuration for the iOS
/// framework depending on the environment
/// variables set by Xcode 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)
/// generate a helpful ./gradlew wrapper with embedded Java path
doLast {
  val gradlew = File(targetDir, "gradlew")
  gradlew.writeText("#!/bin/bash\n"
        + "export 'JAVA_HOME=${System.getProperty("java.home")}'\n"
        + "cd '${rootProject.rootDir}'\n"
        + "./gradlew \$@\n")
  gradlew.setExecutable(true)
}
tasks.getByName("build").dependsOn(packForXcode)
ls -l shared/build/xcode-frameworks

cd "$SRCROOT/../../shared/build/xcode-frameworks"
./gradlew :shared:build -PXCODE_CONFIGURATION=${CONFIGURATION}

xcodeproj=iosApp/HelloKMP

Shared code from iOS

With the iOS app project in place and linked up with the shared project, next you’ll use the shared code in the iOS version of the HelloKMP app. The iOS app will consist primarily of user interface code, relying on the shared code to do most of the work.

import shared
Text(Greeting().greeting())

Challenge

You have a real albeit simple Kotlin Multiplatform app for iOS and Android that uses shared code between the two platforms. Your challenge for this chapter is to add the iOS systemVersion into the greeting in the iOS app. As a hint, you can use UIDevice.currentDevice.systemVersion to obtain the system version.

Key points

  • Kotlin Multiplatform is a new and fast-growing approach to cross-platform app development.
  • KMP lets you share Kotlin code between iOS, Android, web, server, and more.
  • There are a number of advantages to KMP, including developer familiarity with Kotlin, native performance, native UI code, and the consolidation of your app business logic into a shared module across all platforms.
  • You use the expect and actual keywords to create a common interface within the shared code that relies on concrete implementations on different platforms as needed.

Where to go from here?

You’ve just scratched the surface of Kotlin Multiplatform development in this chapter. There are a growing number of resources out there on KMP, so be sure to seek them out to see how to build more realistic apps, including doing things like networking, parsing JSON, and storing data locally in your app.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now