Early Kotlin Multiplatform Thoughts

How often have you or your team needed native mobile apps, but are stretched too thin by keeping them in parallel? How often has there been a battle over backend and frontend data models? Kotlin Multiplatform to the rescue! Kotlin Multiplatform (KMP) is a set of libraries that can be used to share business logic across different platform applications using Kotlin as the shared development language. KMP libraries reached Alpha development status in August 2020 with the release of Kotlin 1.4, and have had steady releases every couple months since. The main use cases for applying these libraries are native iOS/Android pair apps, and a web browser client/backend server relationship. In both of these cases logic such as networking and data models would be built so that both platforms can share them. This post will discuss KMP and Kotlin Multiplatform Mobile (KMM) and present perspectives on using KMM for iOS and Android development.

Early Days for KMP Wins and Losses

Starting with the good points of using KMP and KMM. Sharing code across multiple platforms not only results in development time savings, but it also significantly reduces the amount of bugs and debugging time that arises when maintaining separate code bases. Along with a personal shared codebase there are KMP libraries that can be leveraged to move common functions like networking into the shared layer.

KMP Shared Library Code Block:

build.gradle.kts

val ktor_version = “1.5.3”
sourceSets {
	val commonMain by getting {
   		dependencies {
       			implementation("io.ktor:ktor-client-core:$ktor_version"
		}
	}
	val androidMain by getting {
	   	dependencies {
	       		implementation("io.ktor:ktor-client-android:$ktor_version")
	   	}
	}
	val iosMain by getting {
	   	dependencies {
	       		implementation("io.ktor:ktor-client-ios:$ktor_version")
	   	}
	}
}

While sharing is caring, there are times where it is better for the platforms to handle operations themselves which KMP integrates with. If there is a library or API that functions better from the native layer KMM can be told to use that implementation. For the UI layer, there is a strict stop on sharing. Each platform is responsible for creating their own distinct UI that fits into their ecosystems. While KMM is not exactly a hybrid development approach, many of the complaints made using those kinds of frameworks are answered by KMM.

With positives must also come negatives. KMP has only been around for a few years as an experimental feature, and only reached alpha in mid 2020. Since it is a new technology, certain aspects may not be stable or fully supported. With any new tech the community of developers is growing, but finding support for common issues could be difficult. Developers coming from iOS will most likely struggle the most with adopting KMM. Android developers have been programming in Kotlin for years, but iOS developers not familiar with the language may be frustrated with the differences between Kotlin and Swift. Debugging in iOS may also be more difficult due to generic and unclear error logs, and using Cocoapods is a bit of a process to set up. Android developers do not escape unscathed in the process. Android devs looking for a quick iOS fix will be disappointed as access to Xcode and Swift is still required for building the iOS UI and implementing any native functions/APIs that KMM cannot handle. Both sets of developers will run into some slowdowns when deciding on libraries. Research will be required to find out if there is a KMP library that can replace native libraries. In the end, unless one cross platform developer can handle the workload of the entire project, there still is a need for a cross functional team to handle each platform, and possibly an extra person to handle the shared only.

No Hybrids Here!

While as much code as possible should be kept in the Kotlin shared code, sometimes there are native APIs or libraries that are much better suited to accomplish a task. KMP handles the need for native implementations with ease. There are a set of keywords that are used to denote a place where native code will be used. In the shared code, the keyword “expected” is used to tell the compiler that it needs to find an implementation in the native code for this expected function/variable/ect. The keyword paired to this is “actual.” In your native code, using actual tells the compiler that this is what the actual execution of the expected code is.

File: commonMain, GenerateUserId

expect class GenerateUserId {
   	fun generateUUID(): String
}

File: commonAndroid, GenerateUserId

import java.util.*

actual class GenerateUserId {
   	actual fun generateUUID(): String {
       		val id = UUID.randomUUID()
       		return id.toString()
   	}
}

File: commoniOS, GenerateUserId

import platform.Foundation.*

actual class GenerateUserId {
   	actual fun generateUUID(): String {
       		val id = NSUUID.UUID()
       		return id.toString()
   	}
}

In the case of using native libraries, they are declared in the shared gradle file, and specified to the platform. Android remains the same as usual since it is still a gradle implementation, and this process allows the use of Cocapods for iOS.

File: build.gradle.kts: 

plugins {
   kotlin("multiplatform")
   kotlin("native.cocoapods")
   id("com.android.library")
   id("com.google.gms.google-services")
}

version = "1.0"

cocoapods {
   summary = "Test"
   homepage = "Test"
   pod("FirebaseAnalytics")
   frameworkName = "TestFramework"
}

val androidMain by getting {
   dependencies {
       implementation("com.google.android.material:material:1.2.1")
       implementation("io.ktor:ktor-client-android:$ktor_version")
       implementation("com.google.firebase:firebase-bom:27.1.0")
       implementation("com.google.firebase:firebase-analytics-ktx")
   }
}

Cross Dev Perspective

One perspective on using KMP and KMM can be taken from the viewpoint of a native iOS/Android cross platform developer. The dev would be familiar with all the tools and intricacies of iOS and Android stacks that are needed to write the shared logic and create native pieces. With the shared code base, they would save time by only writing business code once and have some breathing room to concentrate on great UI and UX. When it comes to libraries, it would still require some KMP research for a shared library option, but they would be more well informed on what their options are for native. Finally, their experience with both platforms would allow them to fit anywhere on a team building in KMM. For a cross platform dev, the benefits of using KMM would greatly outweigh developing specific iOS and Android app pairs.

Final Thoughts

Kotlin Mutliplatform is still in its early days of development as of this posting in May 2021, and it shows. Especially where native iOS pods are concerned. When researching and coding how to access Cocoapods from commoniOS, there were a few errors that cropped up, and there was very little on how to fix them because of the small community. On the other side, when using native iOS code, it was painless to access the built-in libraries. From the Android side, everything seems to be functioning easily and as expected from sharing Kotlin and the Android set up. With the knowledge that this technology is still in alpha, some of this is expected as growing pains and should stabilize with more releases. Overall, KMP answers complaints that people have about hybrid apps and web building, and does so using a modern language with minimal boilerplate. As KMP becomes more stable and closer to release, the future looks bright for it becoming a development staple across all spaces.