This is a basic Kotlin Multiplatform app template for Android and iOS. It includes shared business logic and data handling, and a shared UI implementation using Compose Multiplatform.
The template is also available with native UI written in Jetpack Compose and SwiftUI.
The
amperbranch showcases the same project configured with Amper.
The data displayed by the app is from The Metropolitan Museum of Art Collection API.
The app uses the following multiplatform dependencies in its implementation:
- Compose Multiplatform for UI
- Ktor for networking
- kotlinx.serialization for JSON handling
- Kamel for image loading
- Koin for dependency injection
These are just some of the possible libraries to use for these tasks with Kotlin Multiplatform, and their usage here isn't a strong recommendation for these specific libraries over the available alternatives. You can find a wide variety of curated multiplatform libraries in the kmp-awesome repository.
To setup Koin Annotations & KSP you need the following:
Gradle Toml file update:
Compose App Build Gradle update:
alias(libs.plugins.ksp)api(libs.koin.annotations)Extra KSP Configurations:
sourceSets.named("commonMain").configure {
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
}dependencies {
add("kspCommonMainMetadata", libs.koin.ksp.compiler)
add("kspAndroid", libs.koin.ksp.compiler)
add("kspIosX64", libs.koin.ksp.compiler)
add("kspIosArm64", libs.koin.ksp.compiler)
add("kspIosSimulatorArm64", libs.koin.ksp.compiler)
}project.tasks.withType(KotlinCompilationTask::class.java).configureEach {
if(name != "kspCommonMainKotlinMetadata") {
dependsOn("kspCommonMainKotlinMetadata")
}
}All Koin configuration is available here: Koin.kt
Inside this configuration file:
- All definitions are available as module classes, annotated with
@Module - The
@ComponentScanallows scanning annotated components for a given package (across Gradle modules) - Class components are tagged with
@Singleor@KoinViewModel - Some components are defined inside annotated Kotlin functions (Json & Http Client)
The NativeModule class allow to share native components with expect/actual mechanism. The idea is to inject a native implementation of PlatformComponent class.
Here is how it's organized:
In common Kotlin sourceSet:
@Module
expect class NativeModuleexpect class PlatformComponent {
fun sayHello() : String
}In Android sourceSet:
@Module
@ComponentScan("com.jetbrains.kmpapp.native")
actual class NativeModule@Single
actual class PlatformComponent(val context: Context){
actual fun sayHello() : String = "I'm Android - $context"
}In iOS sourceSet:
@Module
@ComponentScan("com.jetbrains.kmpapp.native")
actual class NativeModule@Single
actual class PlatformComponent{
actual fun sayHello() : String = "I'm iOS"
}Let's create an Id generator class that will receive some a prefix, to generate ids:
@Factory
class IdGenerator(@InjectedParam private val prefix : String) {
fun generate() : String = prefix + KoinPlatformTools.generateId()
}We are using @InjectedParam to indicates that prefix property will be dynamically injected via a call with parametersOf.
Later in our code, lets call our generator:
val idGen = KoinPlatform.getKoin().get<IdGenerator> { parametersOf("_prefix_") }.generate()
println("Id => $idGen")It should produces something like _prefix_d1a7ac22-0aee-4f3c-9d5c-41e6bfae5676
To allow the use of Koin in multiplatform style, but allow some special configuration (like injecting Android context), we can allow this kind of startup function:
// config allow to extend Koin configuration from caller side
fun initKoin(config : KoinAppDeclaration ?= null) {
startKoin {
modules(
AppModule().module,
)
// call for any extra configuration
config?.invoke(this)
}
}On Android startup:
class MuseumApp : Application() {
override fun onCreate() {
super.onCreate()
initKoin {
androidContext(this@MuseumApp)
}
}
}