Thursday 30 September 2021

Cannot inject context into cookie manager with Kodein

I have the MVVM app build with KMM. ViewModel contains several use cases. Each use case calls methods of Repository and Repository calls NetworkService to execute the API call. Use cases, Repository and NetworkService are in the shared module. I need to store all cookies in shared preferences which I receive from the server. For this purpose I created my own cookies storage and installed it in this way:

private val httpClient = HttpClient {
        install(HttpCookies) {
            storage = CookiesStorage(di)
        }
    }

Here's the code of cookies storage:

class CookiesStorage(val di: DI) : CookiesStorage {

    private val cookiesStorageImpl = CookiesStorageImpl(di)

    override suspend fun addCookie(requestUrl: Url, cookie: Cookie) {
        cookiesStorageImpl.addCookie(requestUrl, cookie)
    }

    override fun close() {
    }

    override suspend fun get(requestUrl: Url) = cookiesStorageImpl.getCookies()
}

expect class CookiesStorageImpl(di: DI) {
    val di: DI
    fun addCookie(requestUrl: Url, cookie: Cookie)
    fun getCookies(): MutableList<Cookie>
}

As work with storing key-value data in iOS and Android is different, I added expect class CookiesStorageImpl. Android implementation of this class is now the following:

actual class CookiesStorageImpl actual constructor(actual val di: DI) {

    private val cookieMap = mutableMapOf<String, String>()

    private val context: Context by di.instance()

    actual fun addCookie(requestUrl: Url, cookie: Cookie) {
        println("Set cookie name=${cookie.name}, value=${cookie.value}")
        cookieMap[cookie.name] = cookie.value
        context.getSharedPreferences("kmm_preferences", Context.MODE_PRIVATE)
    }

    actual fun getCookies() = mutableListOf<Cookie>().apply {
        cookieMap.forEach {
            this.add(Cookie(it.key, it.value))
        }
    }

}

As you can see, I initialize the context with di here.

Here's the di graph in android app:

val appModule = DI.Module("app module") {
    import(viewModelModule)
    bind<Context>() with multiton { app: App ->
        app.applicationContext
    }
}

val viewModelModule = DI.Module("view model module") {
    import(useCaseModule)

    bind<ViewModelProvider.Factory>() with singleton {
        ViewModelFactory(instance())
    }
    bind<LoginViewModel>() with provider {
        LoginViewModel(instance())
    }
}

And here's the DI of shared module:

val useCaseModule = DI.Module("use case module") {
    bind<LoginUseCase>() with singleton {
        LoginUseCase(di)
    }
}

So, as you can see, I just pass di from use case to CookiesStorageImpl. But when I run the app I got the following error while accessing to context:

org.kodein.di.DI$NotFoundException: No binding found for bind<Context> { ? { ? } }

So, as I understand, the problem is that UseCase doesn't know anything about the context, but I cannot understand how can I pass the binding to the use case module. Thanks in advance for any help!

UPD

Here's the way how I add graph in the Application class:

class App : Application(), DIAware {

    override val di by DI.lazy {
        import(appModule)
    }

}


from Cannot inject context into cookie manager with Kodein

No comments:

Post a Comment