attempts on creating the best hybrid mobile framework in the past. • Most notable candidates include React Native, Flutter, Xamarin, and Ionic. • In order to understand why KMM is a gamechanger, we first have to briefly look into these past solutions.
be extended to time saved on writing or managing one codebase. • Fast - in shipping, in deploying, and in developing. • Team size can be kept small, in return saving business expense. • Slave to the framework. • Starts fragmenting when TLOC gets bigger. The Good, The Bad, And The Ugly
The Question Remains Kotlin, a JVM based language, is very rapidly being adopted by the android community. Based on the demographic android covers, Kotlin would be an easy candidate for a hybrid framework.
development provided by JetBrains. It uses the multiplatform capabilities of Kotlin and includes various tools and features designed to make the end-to-end experience of building mobile cross-platform applications as enjoyable and efficient as possible.”
shared among all the platforms shared module will hold the shared business logic. This logic can be further dissected into platform specific directories androidMain & iosMain directories will hold the business logic that will be shared among the respective platform’s feature module
• If iOS specific-code needs to be written, then Xcode version 11.3 or higher • Compatible Kotlin compiler plugin • Kotlin Multiplatform Mobile plugin in Android Studio • JDK
shared among all the platforms shared module will hold the shared business logic. This logic can be further dissected into platform specific directories androidMain & iosMain directories will hold the business logic that will be shared among the respective platform’s feature module
val missionName: String, @SerialName("launch_year") val launchYear: Int, @SerialName("launch_date_utc") val launchDateUTC: String, @SerialName("rocket") val rocket: Rocket, @SerialName("details") val details: String?, @SerialName("launch_success") val launchSuccess: Boolean?, @SerialName("links") val links: Links ) @Serializable data class Rocket( @SerialName("rocket_id") val id: String, @SerialName("rocket_name") val name: String, @SerialName("rocket_type") val type: String ) @Serializable data class Links( @SerialName("mission_patch") val missionPatchUrl: String?, @SerialName("article_link") val articleUrl: String? )
missionName: String, @SerialName("launch_year") val launchYear: Int, @SerialName("launch_date_utc") val launchDateUTC: String, @SerialName("rocket") val rocket: Rocket, @SerialName("details") val details: String?, @SerialName("launch_success") val launchSuccess: Boolean?, @SerialName("links") val links: Links ) @Serializable data class Rocket( @SerialName("rocket_id") val id: String, @SerialName("rocket_name") val name: String, @SerialName("rocket_type") val type: String ) @Serializable data class Links( @SerialName("mission_patch") val missionPatchUrl: String?, @SerialName("article_link") val articleUrl: String? ) Entity.kt(com.kdhillon.spacex.shared.entity)
... } sqldelight { database("AppDatabase") { // The packageName parameter specifies the package name for the generated Kotlin sources. packageName = "com.kdhillon.spacex.shared.cache" } }
sqldelight { database("AppDatabase") { // The packageName parameter specifies the package name for the generated Kotlin sources. packageName = "com.kdhillon.spacex.shared.cache" } } Build.gradle.kts (shared)
TEXT NOT NULL, launchYear INTEGER AS Int NOT NULL DEFAULT 0, rocketId TEXT NOT NULL, details TEXT, launchSuccess INTEGER AS Boolean DEFAULT NULL, launchDateUTC TEXT NOT NULL, missionPatchUrl TEXT, articleUrl TEXT ); CREATE TABLE Rocket ( id TEXT NOT NULL PRIMARY KEY, name TEXT NOT NULL, type TEXT NOT NULL );
NOT NULL, launchYear INTEGER AS Int NOT NULL DEFAULT 0, rocketId TEXT NOT NULL, details TEXT, launchSuccess INTEGER AS Boolean DEFAULT NULL, launchDateUTC TEXT NOT NULL, missionPatchUrl TEXT, articleUrl TEXT ); CREATE TABLE Rocket ( id TEXT NOT NULL PRIMARY KEY, name TEXT NOT NULL, type TEXT NOT NULL ); AppDatabase.sq
TEXT NOT NULL, launchYear INTEGER AS Int NOT NULL DEFAULT 0, rocketId TEXT NOT NULL, details TEXT, launchSuccess INTEGER AS Boolean DEFAULT NULL, launchDateUTC TEXT NOT NULL, missionPatchUrl TEXT, articleUrl TEXT ); CREATE TABLE Rocket ( id TEXT NOT NULL PRIMARY KEY, name TEXT NOT NULL, type TEXT NOT NULL );
private val dbQuery = database.appDatabaseQueries } Database.kt(com.kdhillon.spacex.shared.cache) expect class DatabaseDriverFactory { fun createDriver(): SqlDriver }
private val dbQuery = database.appDatabaseQueries } Database.kt(com.kdhillon.spacex.shared.cache) actual class DatabaseDriverFactory(private val context: Context) { actual fun createDriver(): SqlDriver { return AndroidSqliteDriver(AppDatabase.Schema, context, "test.db") } }
private val dbQuery = database.appDatabaseQueries } Database.kt(com.kdhillon.spacex.shared.cache) actual class DatabaseDriverFactory(private val context: Context) { actual fun createDriver(): SqlDriver { return AndroidSqliteDriver(AppDatabase.Schema, context, "test.db") } }
private val dbQuery = database.appDatabaseQueries } Database.kt(com.kdhillon.spacex.shared.cache) actual class DatabaseDriverFactory(private val context: Context) { actual fun createDriver(): SqlDriver { return AndroidSqliteDriver(AppDatabase.Schema, context, "test.db") } } actual class DatabaseDriverFactory { actual fun createDriver(): SqlDriver { return NativeSqliteDriver(AppDatabase.Schema, "test.db") } }
private val dbQuery = database.appDatabaseQueries } Database.kt(com.kdhillon.spacex.shared.cache) actual class DatabaseDriverFactory(private val context: Context) { actual fun createDriver(): SqlDriver { return AndroidSqliteDriver(AppDatabase.Schema, context, "test.db") } } actual class DatabaseDriverFactory { actual fun createDriver(): SqlDriver { return NativeSqliteDriver(AppDatabase.Schema, "test.db") } }
private val dbQuery = database.appDatabaseQueries } Database.kt(com.kdhillon.spacex.shared.cache) actual class DatabaseDriverFactory(private val context: Context) { actual fun createDriver(): SqlDriver { return AndroidSqliteDriver(AppDatabase.Schema, context, "test.db") } } actual class DatabaseDriverFactory { actual fun createDriver(): SqlDriver { return NativeSqliteDriver(AppDatabase.Schema, "test.db") } }
and the public API’s are expected to be changed frequently given its scale. • Deploying a KMM project in production therefore should be prohibited. • A lot of edge-cases might be encountered when working in KMM, such as how to manage sensitive information. • iOS counterpart receive the shared module as a framework dependency, where the class files are in Objective-C format. Even though using appCode IDE helps a bit in navigating these Obj-C files, having them in Swift would be the ideal case.