3

Recently I published a library with koin; when I used this library in my own application, everything is ok because I don't use Koin in my application, but if I startKoin in my application and the library both, the app crashes!.

Is there any way to use koin in application and library same? how can I call startKoin in my library without facing any problem in apps that used koin and called startKoin?

org.koin.core.error.KoinAppAlreadyStartedException: A Koin Application has already been started

hamid Mahmoodi
  • 690
  • 4
  • 16

2 Answers2

4

After some research, I found that I should use a custom KoinComponent.

Based on the documentation:

For SDK Makers, you can also work with Koin in a non global way: use Koin for the DI of your library and avoid any conflict by people using your library and Koin by isolating your context

// create a KoinApplication
val myApp = koinApplication {
// declare used modules
modules(coffeeAppModule)
}

And keep myApp in a object:

// Get a Context for your Koin instance
object MyKoinContext {
var koinApp : KoinApplication? = null
}

// Register the Koin context
MyKoinContext.koinApp = KoinApp 

and then :

interface CustomKoinComponent : KoinComponent {
// Override default Koin instance, intially target on GlobalContext to yours
override fun getKoin(): Koin = MyKoinContext?.koinApp.koin

}

And now, you register your context and run your own isolated Koin components:

MyKoinContext.koinApp = myApp

class ACustomKoinComponent : CustomKoinComponent(){
// inject & get will target MyKoinContext
}

more info

hamid Mahmoodi
  • 690
  • 4
  • 16
2

I don't think a library should have any dependency injection framework in it, with the exception if the library is only used by your own projects. Choosing a dependency injection framework is an application-wide architectural decision and a library should not force any specific choice for users of the library.

My suggestion is to remove koin from your library completely.

The crash is just a symptom of the design problem: a library making application-wide decisions. Removing koin from the library also gets rid of the crash.

Conceptually you can use dependency injection in your library. For example, have dependencies as constructor arguments and allow callers to use any DI framework of their choosing (or none at all) to wire up the dependencies.


Edit: Yes, a custom koin component can be used to isolate koin in a way that is mostly internal to the library. There are still hidden dependencies that affect the top level application, for example gradle dependency resolution can update koin to an incompatible version - there have been breaking changes in semantically versioned patch/minor updates in koin's past and there can be more in the future.

I consider the architectural advice still pretty much valid: a generic, reusable library should not come with heavy dependencies such as a DI tool. And not having koin in the library is one way to solve the original problem.

laalto
  • 150,114
  • 66
  • 286
  • 303
  • Why should he remove koin? Just use a custom Koin component and everything works fine. – Sebastian Rieger Jan 26 '22 at 19:37
  • You are incorrect; the library in this question is not forcing any DI choice on to the consumer, it is just incorrect usage of koin for the library use case. Hamid's answer is correct – Thomas Cook May 24 '22 at 14:17
  • @laalto I'm not sure why you think Koin is forcing `any specific choice for users of the library` and `making application-wide decisions`. Can you elaborate what you meant by this? I am using Koin in a library and we have not seen this behaviour anywhere if there is proper Context Isolation in place. – Abdul Mateen Jan 06 '23 at 15:19
  • 1
    @AbdulMateen At the original time of writing the answer wasn't aware of the custom koin component / context isolation, so that would have meant that using your library would require to accept your choice of DI tooling and its consequences. With context isolation some of that can be hidden. The isolation is not perfect, there are still some parts where the choices leak, for example with gradle dependency versioning updating a dependency to a potentially unsupported one - have had broken semver issues with koin too. – laalto Jan 06 '23 at 19:02
  • 1
    I consider the architectural advice still very much valid: a generic, reusable library should not come with heavy dependencies such as a DI tool. If the library needs one, it's likely too complex to be generic and reusable. Since the answer is old with some value, not editing it heavily or deleting it. – laalto Jan 06 '23 at 19:03
  • @laalto ah I see. Yes, I agree that having a DI framework does make your library less generic. Also, if you could add your above comment as a PS/Note in your answer, I would be able to change my vote. At the moment, my downvote is locked by SO unless you edit your answer. – Abdul Mateen Jan 07 '23 at 20:15