Can anyone guide me on how DI should be handled in...
# multiplatform
h
Can anyone guide me on how DI should be handled in KMM projects? In the shared module, I’ve created my usecase that is dependent on a repository object (also repository is dependent on other objects). I use Koin in android and shared module and everything works well. But in iOS I don’t have any idea how to inject the repository into the usecase.
t
Our team is going to run into this soon as well, we're hoping to learn from the KaMPKit project: https://github.com/touchlab/KaMPKit/blob/main/ios/KaMPKitiOS/Koin.swift
r
There is a koin channel here and just this morning I asked the same question, no response yet
j
fwiw am using Koin for shared code in https://github.com/joreilly/PeopleInSpace
🙏 1
In this case it's the repository that's exposed directly but should work same way for a usecase layer
🙏 1
The repository in this case extends
KoinComponent
...and there's also koin initialization function that's called from iOS code
🙏 1
h
@John O'Reilly thank you. Your solution helped me. @Tiago Nunes & @Rak I suggest you to check it out too. Also this article could be useful https://johnoreilly.dev/posts/kotlinmultiplatform-koin/
👍 3
g
@Hossein Amini My 2 cents, I use Koin for the shared module and Hilt for androidApp. Why two? Well first I wanted to try if it was possible, in theory since one is compile time and other run time, it shouldn’t be a problem. Second, I prefer Hilt over Koin. So far so good, no problem found.
h
@Guilherme Delgado I didn’t know that’s possible 🤔 On the iOS, is it possible to use native DI libraries to inject my use-cases too?
g
Me neither, that’s why I’ve tried and was happy with the result ^^ You don’t have to, just setup your koin dependencies receiving their parameters as field injection instead of constructor injection and you’re good to go (for shared instances)
But yeah if your iOSCode (not shared) needs some kind of DI framework, sure it will work.
see the shared module as a pod (black box)
j
@Guilherme Delgado how are you injecting dependency from shared code in to say android vm etc if mixing di frameworks like this?
g
Ok let me share my logic: module shared/… Di setup:
Copy code
// called by iOS etc
fun initKoin(enableNetworkLogs: Boolean) = initKoin(enableNetworkLogs) {}

fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclaration = {}) =
    startKoin {
        appDeclaration()
        modules(commonModule(enableNetworkLogs = enableNetworkLogs))
    }

private fun commonModule(enableNetworkLogs: Boolean) = module {
...
    single { SettingsApi(get(), PlatformDependencies().baseUrl) }
    single { SettingsManager() }
}
Classes:
Copy code
class SettingsManager: KoinComponent {

    private val api: SettingsApi by inject()
    ...
}

class SettingsApi(private val client: HttpClient, private val baseUrl: String) : KoinComponent {...}
shared/androidMain:
Copy code
...
lateinit var buildConfigApiUrl: String

actual class PlatformDependencies actual constructor() {

    actual val baseUrl: String = buildConfigApiUrl

    ...
}
module androidApp/…
Copy code
class App : Application() {

    override fun onCreate() {
        super.onCreate()
        ...
        buildConfigApiUrl = BuildConfig.API_URL
        initKoin(BuildConfig.DEBUG) {
            androidLogger()
            androidContext(this@App)
        }
    }
}
Di setup:
Copy code
@Module
@InstallIn(SingletonComponent::class)
object AppModule {

...

    @Provides
//    @ViewModelScoped
    @Singleton
    fun provideSettingsManager() = SettingsManager()
}
Copy code
@HiltViewModel
class MyViewModel @Inject constructor(
    private val settingsManager: SettingsManager,
    ...
) : ... {
@John O'Reilly Please feel free to comment and validate, this was something that I wanted to bring into discussion. It’s working, but maybe I’m getting here a false positive or it works in this specific use case 😉
j
ok, so
SettingsManager
is being separately managed as dependency in both Hilt and Koin?
g
I would say yes. Actually in the shared module no one calls SettingsManager directly so this: 
single { SettingsManager() }
 ends up being the iOSApp DI setup. 🤔
the quickest downside I see for Android is the DI replication for the layer that will be used by VM
j
yeah, and also just having an extra library dependency to manage like this has some overhead....I had started using Hilt in one repo I had, but reverted to Koin as I was seeing too many issues as I moved though Compose updates
g
well so far I haven’t experienced this kind of issues (even with compose updates), but sure the compile time will have overhead and also managing two DI libs.
j
I initially ran in to following but was able to use workaround https://github.com/google/dagger/issues/2631
👍 1
g
since this are small POC projects so far so good. But if I had to use in PRD I guess I would go full Koin.
oh a fun fact. I was using Android Studio 4.2.1 with compose compiler plugin in “hack”
j
I had used that approach while we needed Canary for Compose but once it was available in beta then I switched to that so that could take advantage of AS tooling etc
g
dunno if it kept me away, and also the buildtools were 4.2.1
but now I’ve updated to arctic fox since 1.5.0
buildtools 7.1.0-alpha02
everything smooth so far 😅
Copy code
object Jetbrains {
        const val kotlin = "1.5.0"
        const val kotlinGradlePlugin = "1.5.10"
        const val ktor = "1.6.0"
        const val kotlinxSerialization = "1.2.1"
        const val kotlinxCoroutines = "1.5.0"
        const val kotlinxDateTime = "0.2.0"
    }

object Core {
            const val hilt = "2.37"
            const val hiltCompose = "1.0.0-alpha03"
            ...
}

object Compose {
            const val core = "1.0.0-beta09"
            const val activity = "1.3.0-beta02"
            const val accompanist = "0.12.0"
            const val paging = "1.0.0-alpha10"
            const val constraintLayout = "1.0.0-alpha08"
            const val navigation = "2.4.0-alpha03"
        }
object Gradle {
            const val buildtools = "7.1.0-alpha02"
        }
^ for reference 🙂
k
@Hossein Amini yes I'm using needle by User for my IOS code atm