https://kotlinlang.org logo
#koin
Title
# koin
s

Saher Al-Sous

04/02/2024, 8:31 AM
Good Morning. did anybody faced an issue in Injecting Context from Android into SqlDelight Driver using koin for a KMP project? I have a Kotlin Multi-platform project, that has iOS, Android and Desktop in it, and I wanted to use SqlDelight to store data. however I'm using Koin as a DI. Since I have several platforms, I made the
commonMain
to
startKoin
. This file contains the expect value that will return the
SqlDriver
for each platform, and the needed implementation to communicate with the database from
commonMain
.
Copy code
expect val dbModule: Module

val repoModel = module {
    single<DbRepository> {
        DbRepository(get<SqlDriver>())
    }
}

fun initKoin() = startKoin {
    modules(
        dbModule,
        repoModel
    )
}
I found out that each platform have an Issue, so I decided to focus on one platform at a time. the problem of the Android platform is that you can't get the context needed for the
AndroidSqliteDriver
.
Copy code
actual val dbModule: Module = module {
    single<SqlDriver> {
        AndroidSqliteDriver(Kos.Schema, androidContext(), "Kos")
    }
}
for Android, you need the context to initialize the driver, but you have no way to inject it. since I'm starting Koin from
commonMain
, I couldn't find a way to get the android context from the
Application
class. I tried this way:
Copy code
class MyApp: Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyApp)
        }
    }
}
also this way
Copy code
class MyApp: Application() {
    override fun onCreate() {
        super.onCreate()
        koinApplication {
            androidContext(this@MyApp)
        }
    }
}
and even this way
Copy code
class MyApp: Application() {
    override fun onCreate() {
        super.onCreate()
        loadKoinModules(
            module { 
                single { this@MyApp }
            }
        )
    }
}
and I always get an error...
Caused by: org.koin.core.error.KoinAppAlreadyStartedException: A Koin Application has already been started
Caused by: org.koin.android.error.MissingAndroidContextException: Can't resolve Context instance. Please use androidContext() function in your KoinApplication configuration.
how is it possible to get android context for SqlDelight driver within kmp using Koin?
probably should get the context manually outside Koin?
a

arnaud.giuliani

04/02/2024, 11:42 AM
in your start for Koin on Android, you need to add
androiContext()
to your startKoin section
s

Saher Al-Sous

04/02/2024, 5:16 PM
I did that in one of the things I tried... 'androidContext(this@MyApp)' but that didn't work, simply because I can't start koin from 2 places. I can start it from android and get the context but it won't be okey for other platforms, I think the only way I have now is manually with 'lateinit' value that captures the context on startup.
a

arnaud.giuliani

04/03/2024, 7:58 AM
start can be different depending on your native target. You can reuse a common shared module
s

Saher Al-Sous

04/03/2024, 8:51 AM
I think i will use the
lateinit
for now. without Koin inside Android.
a

arnaud.giuliani

04/03/2024, 10:56 AM
you can reuse Koin instance and use
declare()
to add an object inside the current context
s

Saher Al-Sous

04/03/2024, 2:11 PM
Thank you so much for telling me about this, never heard of it before... I think I'm not sure how to implement it: I did like this:
Copy code
class MyApp: Application() {
    override fun onCreate() {
        super.onCreate()
        val module = module {
            single { applicationContext }
        }
        getKoin().declare(
            module
        )
    }
}
and this
Copy code
class MyApp: Application() {
    override fun onCreate() {
        super.onCreate()
        getKoin().declare(
            applicationContext
        )
but I got same error on both ways:
Copy code
java.lang.IllegalStateException: KoinApplication has not been started
a

arnaud.giuliani

04/03/2024, 4:00 PM
use it with the target object, here your context:
koin.declare(context)
or koin.declare(this) for your appContext
s

Saher Al-Sous

04/03/2024, 6:36 PM
I tried that. it didn't work as well... the only thing that worked for me is doing it like this:
Copy code
@SuppressLint("StaticFieldLeak")
lateinit var androidContext: Context

actual val dbModule: Module = module {
    single<Context> { androidContext }
    single<SqlDriver> {
        AndroidSqliteDriver(ReminderDb.Schema, get(), "ReminderDb")
    }
}
Copy code
class MyApp: Application() {
    override fun onCreate() {
        super.onCreate()
        androidContext = applicationContext
    }
}
7 Views