top of page
Search

Dependency Injection with Koin in Compose Multiplatform - by Nimesh Vasani. 🧑🏻‍💻

  • Writer: nimesh Vasani
    nimesh Vasani
  • Aug 1, 2024
  • 3 min read

Updated: Aug 3, 2024

In modern software development, managing dependencies effectively is crucial for creating scalable and maintainable applications. Dependency Injection (DI) is a design pattern that helps achieve this by promoting loose coupling and enhancing code modularity. In this post, we'll explore what dependency injection is and how to implement it using Koin in a Compose Multiplatform project.


What is Dependency Injection?



ree


Dependency Injection is a design pattern that involves injecting dependencies (such as services, repositories, or any other objects) into a class, rather than having the class create its own dependencies. This approach offers several benefits:


  1. Loose Coupling: Classes are not tightly bound to specific implementations, making it easier to swap out components.

  2. Testability: It becomes easier to test classes in isolation by providing mock dependencies.

  3. Scalability: Managing dependencies in large projects becomes more straightforward.



Introduction to Koin


Koin is a lightweight dependency injection framework for Kotlin developers. It is designed to be simple, practical, and efficient. Koin integrates seamlessly with Kotlin Multiplatform projects, making it an excellent choice for Compose Multiplatform applications.


ree


Setting Up Koin in Compose Multiplatform


To get started with Koin in a Compose Multiplatform project, follow these steps:



1. Add Koin Dependencies


First, you need to add Koin dependencies to your build.gradle.kts files.


For the common module, add the following dependencies:


On libs.versions.toml:


koin = "3.6.0-alpha3"

koinCompose = "3.6.0-alpha3"

koinComposeMultiplatform = "1.2.0-alpha3"


koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }

koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref ="koinCompose"}

koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }

koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koinComposeMultiplatform" }



On shared/build.gradle.kts, dependencies are configured for both commonMain and androidMain:


commonMain.dependencies { ....

api(libs.koin.core)

implementation(libs.koin.compose)

}



androidMain.dependencies {

...

    implementation(libs.koin.android)

implementation(libs.koin.androidx.compose)    

}




2. Define Your Modules


Create a Kotlin file to define your Koin modules. A module in Koin is a container that holds definitions of how to create and inject dependencies.


val appModule = module {   

  single { Firebase.auth }   

 single { AuthRepository(Firebase.auth) }  

  single { FireStoreRepository(Firebase.firestore,Firebase.auth)}

}



Here, we have defined a module appModule that provides a Repository instance as a singleton.



// Two ways to define viewModel Modules(this is more efficient)


//common main side

expect val viewModelModule: Module


//android main

actual val viewModelModule = module {    viewModelOf(::MainViewModel)    viewModelOf(::FirebaseAuthViewModel)    viewModelOf(::FireStoreViewModel)

}


//iOS main

actual val viewModelModule = module {    

singleOf(::MainViewModel)    

singleOf(::FirebaseAuthViewModel)    

singleOf(::FireStoreViewModel)

}


3.Start koin by inserting into Application level


Koin Application is used for start a new koin application in compose context.


@Composable

@Preview

fun App() {    

val scope = rememberCoroutineScope()    

KoinApplication(application = {      

modules(listOf(appModule,viewModelModule)) 

}

)

  MaterialTheme { 

HomeScreen()

 

}


}



Kotlin serialization consists of a compiler plugin, that generates visitor code for serializable classes, runtime library with core serialization API and support libraries with various serialization formats.


  • Supports Kotlin classes marked as @Serializable and standard collections.


Data class


package models

import kotlinx.serialization.SerialName

import kotlinx.serialization.Serializable


@Serializable

@SerialName("user")

data class User( 

val name :String,    

val image:String,    

val groupsId:List<String> = emptyList(),   

val friendsId :List<String> = emptyList()

)



Repository class


class FireStoreRepository(   

 private val fireStore: FirebaseFirestore, firebaseAuth:FirebaseAuth

) {


suspend fun saveUser() {   

 if (firebaseUser != null) {        

fireStore.collection("user").document(firebaseUser.uid).set(

User.serializer(), 

User(

name=firebaseUser.displayName

?:firebaseUser.email!!.dropLast(10),

image = "",

groupsId = listOf(),

friendsId = listOf()),

)

}

}

}


ViewModel for UI state management


class FireStoreViewModel(

private val fireStoreRepository: FireStoreRepository

) : ViewModel() {  


  private var friends = MutableStateFlow<List<User>>(emptyList())     val friends : StateFlow<List<User>> = friends.asStateFlow()


  suspend fun fetchFriends() {       

 viewModelScope.launch {           

 val friendData = fireStoreRepository.fetchFriends()             _friends.value = friendData       

  }    

}

}


Main Compose class


@Composable

fun MainScreen(

fireStoreViewModel: FireStoreViewModel = koinInject()

) { 


   val navController = rememberNavController()        


LaunchedEffect(Unit) {         


// firestoreViewModel.saveUSer()            

fireStoreViewModel.fetchFriends()       

}


val friends = viewModel.friends.collectAsState()


// UI and use of frinds data from viewModel


}


Don’t forget to add Internet permission on Android Manifest file

androidMain (AndroidManifest.xml)


Conclusion


Dependency Injection simplifies the management of dependencies in your Compose Multiplatform projects, promoting better code organization and testability. Koin is a powerful and easy-to-use DI framework that integrates seamlessly with Kotlin Multiplatform, making it an excellent choice for modern application development. By following the steps outlined in this post, you can effectively implement DI in your Compose Multiplatform projects and enjoy the benefits it brings.


Concluded by Nimesh Vasani.

 
 
 

Comments


Available for Select
freelance opportunities

Have an exciting project you need 

help with?

Send me an email or contact me via

instant message!

© 2022 by Nimesh Vasani. Mobile Developer

icons8-quotation-48.png
Screenshot 2024-03-20 at 5.53.00 PM.png

You can't connect the dots looking forward; you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future. You have to trust in something - your gut, destiny, life, karma, whatever. This approach has never let me down, and it has made all the difference in my life.

I’m as proud of many of the things we haven’t done as the things we have done. Innovation is saying no to a thousand things.

- Steve Jobs

  Founder of Apple Inc.

icons8-quotation-48.png
Screenshot 2024-03-20 at 6.01.37 PM.png

Clean code always looks like it was written by someone who cares. Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program

- Robert C Martin 

    Known For Agile Manifesto.

icons8-quotation-48.png
Screenshot 2024-03-20 at 6.01.59 PM.png

When I wrote this code, only God and I understood what I did. Now only God knows. 

 – Anonymous

bottom of page