Saturday, June 11, 2022
HomeWeb DevelopmentConstructing cross-platform cellular apps with Kotlin Multiplatform

Constructing cross-platform cellular apps with Kotlin Multiplatform


Think about that you’re an knowledgeable Android developer with an thought for a killer app. You might be assured that you could construct an app for Android simply, however usually are not so positive about iOS.

If in case you have expertise growing cellular UIs for Android, you’ll be able to in all probability observe some SwiftUI tutorials and get by simply fantastic. However what in regards to the core of the app? Even when you’ve got expertise with iOS improvement, rewriting the identical core for iOS will be redundant.

So how will you execute your thought with a minimal studying curve? Enter Kotlin Multiplatform Cellular, or KMM.

Based on their official web site, “Kotlin Multiplatform Cellular (KMM) is an SDK designed to simplify the event of cross-platform cellular purposes.”

With KMM, you’ll be able to write the core of your app in Kotlin and use it in each Android and iOS purposes. You solely want to write down platform-specific code like UI.

On this submit, we’ll be taught KMM by constructing a small note-taking software with native database operations. We’ll additionally see how widespread enterprise logic, like database operations, will be reused. However first, we have to clear some stipulations.

Atmosphere setup

First, we have to set up Android Studio. If we wish to attempt the iOS facet of issues, we’ll want to put in Xcode, too. We are able to construct and run the Android app with KMM code with out Xcode.

Subsequent, we’d like to verify we now have the most recent Kotlin plugin put in. Go to Android Studio Instruments after which hover over Kotlin. Subsequent, click on Configure Kotlin Plugin Updates adopted by Test once more. You’ll be able to see a visible under.

Environment Setup

Languages And Frameworks

After that, seek for the KMM plugin within the Plugin part of the Preferences menu. Click on on Set up Kotlin Multiplatform Cellular plugin and set up it.

Plugins Installed

Evidently, we additionally must have JDK put in. Watch out with this, as it may be a bit difficult. We might must set the JDK set up path in System Paths to get this to work. Fortunately, Stack Overflow has assets and solutions for any troubles.

If it’s important to work with SQLDelight, make certain to put in this plugin as nicely to make it simpler to work with.

Making a KMM mission

Now that the setting is about, let’s create a KMM mission.

If you have already got an Android/iOS mission that you just wish to prolong with KMM, you have to so as to add the KMM module into your Android mission, restructure the code to maneuver widespread code into shared modules, and configure the iOS mission to hyperlink to the KMM framework. You’ll be able to reference this course of right here, however let’s ignore this difficult case whereas beginning out.

First, let’s create a brand new mission in Android Studio by going to File -> New -> New Mission. Then, choose Kotlin Multiplatform App. This’ll come up if we end the setting setup efficiently:

New Project

Subsequent, fill within the package deal title and mission path.

The third step is new, and particular to KMM. Right here, we will rename the Android, iOS, or shared module names. Since we are attempting out KMM for the primary time, let’s preserve these choices the identical.

Allow the checkbox for Add Pattern Assessments for Shared Module if you wish to check out check executions and choose Common Framework for iOS framework distribution. It ought to appear to be this:

New Project View

Now, the mission will probably be prepared very quickly! As soon as it’s set, we will change the Mission Information View from Android to Mission and look at the whole construction. Please remember that quite a bit has occurred behind the scenes, which we’ll focus on subsequent.

KMM mission construction

A KMM mission has three modules: Android, shared, and iOS.

Android module

The Android module is our regular app module that builds into an Android software. In our case, we named it androidApp.

Shared module

This can be a Kotlin Multiplatform module that compiles into an Android library and iOS framework. The shared module holds all of the widespread, reusable code for the app. There are three elements inside it: commonMain, androidMain, and iosMain.

commonMain is just not platform-specific and holds all of the code that may immediately run on each platforms. There are Kotlin libraries which can be KMM-compatible in sure use circumstances. For instance, we’ll use SQLDelight for databases or Ktor for community calls.

In androidMain, some code within the widespread module may have a unique API or conduct. This module holds the Android-specific code.

iosMain is androidMain’s counterpart. It holds the iOS-specific code of commonMain. Having androidMain and iosMain contained in the shared module could seem counterintuitive, however it would develop into clear as soon as we do some hands-on work.

iOS module

This module is an Xcode mission that builds as an iOS software and consumes our shared KMM module as a framework. If you happen to recall, we chosen Common Framework for iOS within the third step of the earlier part.

Please word that though this module is created inside our root mission listing, it isn’t linked to another a part of the mission (aside from consuming the shared module).

Different Modules

How does the Xcode mission know in regards to the shared framework?

As we simply coated, the shared module is compiled into an iOS framework and is then consumed by the iOS app inside the basis mission. However how does it learn about it? Do we have to join them manually? No!

While you create a brand new KMM mission in Android Studio, it robotically configures the construct paths for the framework and different required settings.

Search Paths

Paths

There’s a gradle activity referred to as embedAndSignAppleFrameworkForXcode that executes every time the iOS app is constructed to generate a brand new framework.

Fingers-on with Kotlin Multiplatform Cellular

To solidify the above ideas and see KMM in motion, we are going to construct a small software. Particularly, we’ll construct an on-device database and carry out some operations utilizing widespread code for each Android and iOS.

Particularly for Android, we’ll have a launch display with an inventory of notes retrieved from an on-device database and a CTA for including new notes, seen under:

App Launch Screen

If the consumer clicks Add, the app will take them to a brand new display so as to add a brand new word and put it aside to the database. The app will refresh the primary display with new knowledge, like this:

App Refresh New Data

For iOS, nonetheless, we can have just one display the place we are going to clear the database on every launch, insert new automated notes with timestamp, and fetch and show them on a display:

Automated Note

Disclaimer – I’m not an iOS developer. Since our aim right here is to display how widespread enterprise logic code will be executed on each platforms, doing all three operations on iOS ought to suffice for the aim of this text.

The mission will be cloned from my GitHub repo or you’ll be able to observe alongside and construct a brand new mission. You’ll be able to observe the steps within the “Making a KMM mission” part of this text and create a brand new Android KMM software.

Including dependencies

There aren’t many exterior dependencies for this mission, so we will consider how the dependencies are structured.

Beginning with project-level construct.gradle, this wants the same old gradle-related dependencies together with the SQLDelight-related dependencies.

classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
classpath("com.android.instruments.construct:gradle:7.1.3")
classpath("com.squareup.sqldelight:gradle-plugin:$sqlDelightVersion")

App-level construct.gradle additionally has all the usual gadgets, however there’s one further requirement. We now have so as to add a dependency on the shared module within the dependencies block:

implementation(mission(":shared"))

The shared module construct.gradle has all the foremost modifications:

plugins {
   kotlin("multiplatform")
   id("com.android.library")
   id("com.squareup.sqldelight")
}

kotlin {
   android()


   listOf(
       iosX64(),
       iosArm64(),
       iosSimulatorArm64()
   ).forEach {
       it.binaries.framework {
           baseName = "shared"
       }
   }

   val coroutinesVersion = "1.6.1"
   val ktorVersion = "1.6.1"
   val sqlDelightVersion: String by mission

   sourceSets {
       val commonMain by getting {
           dependencies {
               implementation("com.squareup.sqldelight:runtime:$sqlDelightVersion")
           }
       }
       val commonTest by getting {
           dependencies {
               implementation(kotlin("check"))
           }
       }
       val androidMain by getting {
           dependencies {
               implementation("com.squareup.sqldelight:android-driver:$sqlDelightVersion")
           }
       }
       val androidTest by getting
       val iosX64Main by getting
       val iosArm64Main by getting
       val iosSimulatorArm64Main by getting
       val iosMain by creating {
           dependsOn(commonMain)
           iosX64Main.dependsOn(this)
           iosArm64Main.dependsOn(this)
           iosSimulatorArm64Main.dependsOn(this)
           dependencies {
               implementation("com.squareup.sqldelight:native-driver:$sqlDelightVersion")
           }
       }
       val iosX64Test by getting
       val iosArm64Test by getting
       val iosSimulatorArm64Test by getting
       val iosTest by creating {
           dependsOn(commonTest)
           iosX64Test.dependsOn(this)
           iosArm64Test.dependsOn(this)
           iosSimulatorArm64Test.dependsOn(this)
       }
   }
}

android {
   compileSdk = 31
   sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
   defaultConfig {
       minSdk = 21
       targetSdk = 31
   }
}

sqldelight {
   database("KmmDemoDB") {
       packageName = "com.outliers.kmmdemo.shared.cache"
   }
}

Excluding exterior dependencies declaration, all the pieces else is robotically added whereas creating a brand new KMM mission. We do, nonetheless, want to know varied blocks to know the place so as to add dependencies.

Beginning with the plugins block, discover that we now have utilized the library plugin together with the robotically added multiplatform plugin. The sqldelight plugin is for our database SDK and might not be wanted for different tasks and, thus, must be added manually.

SQLDelight additionally wants the final block to outline the database title and the package deal/path the place queries are outlined. The dependencies wanted for code written within the androidMain of the shared module are declared within the androidMain block inside sourceSets. The identical factor applies for iosMain and commonMain.

Now let’s take a look at the supply code. The crux right here is the shared module. We are going to arrange the database SDK, SQLDelight.

First, create a listing path sqldelight/your-package-name/shared/cache contained in the commonMain folder of the shared module, the place your-package-name is the package deal title you had outlined whereas creating the mission (on this case, the trail is sqldelight/com/outliers/kmmdemo/shared/cache).

Right here, we have to create a file <your database title>.sq that may maintain all our SQL queries, corresponding to insert or choose all. For this demo, I named the file KmmDemoDB.sq.

The database title and package deal title should match with what we now have outlined within the sqldelight block in shared/construct.gradle. SQLDelight makes use of this file to generate code comparable to the offered queries at ~/shared/construct/generated/sqldelight.

If you happen to discover this complicated, please take a while to navigate by way of the mission and determine the recordsdata we’re discussing. You’ll be able to take consolation in the truth that that is solely particular to the SQLDelight library and thus will be totally realized from their documentation.

To really execute the SQL queries in Kotlin widespread code, we’d like an object named SqlDriver. However this SqlDriver is created utilizing totally different APIs for Android and iOS platforms.

Right here comes the function of androidMain and iosMain. We are going to first outline a category referred to as DatabaseDriverFactory, declaring it as count on in widespread sourceset (commonMain):

count on class DatabaseDriverFactory {
   enjoyable createDriver(): SqlDriver
}

The count on key phrase tells the compiler to search for an precise implementation of this class within the platform-specific sourceset (androidMain and iosMain) in the identical package deal (commonMain/com/outliers/kmmdemo/shared/cache) in all sourcesets.

This allows using platform-specific APIs to create SqlDriver, after which all the pieces is similar. Here’s what the precise implementations of androidMain and iosMain appear to be:

precise class DatabaseDriverFactory(personal val context: Context) {
   precise enjoyable createDriver(): SqlDriver {
       return AndroidSqliteDriver(KmmDemoDB.Schema, context, "notes.db")
   }
}

precise class DatabaseDriverFactory {
   precise enjoyable createDriver(): SqlDriver {
       return NativeSqliteDriver(KmmDemoDB.Schema, "notes.db")
   }
}

Please word that we use totally different APIs to create and return an occasion of SqlDriver — specifically, AndroidSqliteDriver and NativeSqliteDriver. These APIs can be found to androidMain and iOSMain by way of the totally different dependencies declared for every sourceset within the construct.gradle file of the shared module.

Now, we’ll create a wrapper class referred to as Database in the identical package deal that internally creates a SqlDriver object utilizing DatabaseDriverFactory and exposes capabilities to do database operations.

class Database(databaseDriverFactory: DatabaseDriverFactory) {
   personal val database = KmmDemoDB(databaseDriverFactory.createDriver())
   personal val dbQuery = database.kmmDemoDBQueries

   inner enjoyable getAllNotes(): Listing<Word> {
       return dbQuery.selectAllNotes().executeAsList()
   }

   inner enjoyable getLastNote(): Word {
       return dbQuery.selectLastNote().executeAsOne()
   }

   inner enjoyable insertNote(title: String, physique: String?) {
       return dbQuery.insertNote(title, physique)
   }

   inner enjoyable deleteAll() {
       return dbQuery.deleteAll()
   }
}

Subsequent, let’s create one other SDK root class. This creates the Database object and exposes all of the operations. This can develop into our level of entry into the shared library for Android/iOS apps.

class KmmSDK(dbDriverFactory: DatabaseDriverFactory) {
   personal val database: Database = Database(dbDriverFactory)

   enjoyable getAllNotes(): Listing<Word> {
       return database.getAllNotes()
   }

   enjoyable getLastNote(): Word {
       return database.getLastNote()
   }

   enjoyable insertNote(title: String, physique: String?) {
       database.insertNote(title, physique)
   }

   enjoyable deleteAll() {
       database.deleteAll()
   }
}

That is the category that Android and iOS apps will instantiate and carry out core work. This class takes in an occasion of DatabseDriverFactory. Thus, when iOS instantiates this class, the compiler will choose the implementation from iosMain.

Override Fun Image

Importing

Now, all it’s important to do is import KmmSDK in Android or shared in iOS and name their strategies, as proven above. The remaining is all UI improvement, which we’re a lot too aware of. Go the notes record to a RecyclerViewAdapter and show it on display. Comparable logic is applied for iOS, too.

Conclusion

Phew! We now have coated a whole lot of floor right here. However, there may be nonetheless one factor to mull over: If we now have written all of our code in Kotlin, how does iOS perceive it?

It’s due to the Kotlin compiler. The Kotlin compiler first converts the Kotlin code to intermediate illustration (bytecode for Android JVM), which, in flip, is transformed into the platform’s native code.

And that’s it! We now have coated a lot of the issues one wants to begin working with Kotlin Multiplatform.

Be at liberty to say hello to me on my LinkedIn and GitHub profiles!

Additional references:

: Full visibility into your net apps

LogRocket is a frontend software monitoring answer that permits you to replay issues as in the event that they occurred in your personal browser. As an alternative of guessing why errors occur, or asking customers for screenshots and log dumps, LogRocket helps you to replay the session to shortly perceive what went unsuitable. It really works completely with any app, no matter framework, and has plugins to log further context from Redux, Vuex, and @ngrx/retailer.

Along with logging Redux actions and state, LogRocket data console logs, JavaScript errors, stacktraces, community requests/responses with headers + our bodies, browser metadata, and customized logs. It additionally devices the DOM to document the HTML and CSS on the web page, recreating pixel-perfect movies of even probably the most complicated single-page apps.

.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments