Any `kotlin inject` users? How is it in production...
# multiplatform
u
Any
kotlin inject
users? How is it in production?
j
See #C0255B8KX7W. Also: great!
u
Oh neat. So is it safe to migrate to from dagger/anvil? The 0.x versions worry me a bit
j
I guess it depends on what your risk model is. We literally built Dagger from nothing, so we have a long history of migrating our DI along. Our kotlin-inject usage is actually entirely in JS, though. We still use are using Dagger 2 within the native host app. I much prefer it as a Kotlin compiler plugin rather than being built on KSP. In some sense that's a lateral move. KSP has a different set of problems than Kotlin compiler plugins ,but both have problems that introduce risk. The last thing is probably that Dagger is maintained within the largest Java/Kotlin codebase on the planet whereas kotlin-inject is maintained (mostly) by one person. That also can be seen as an advantage, though.
u
We still use are using Dagger 2 within the native host app
How come? Do you mean a single class having two
@Inject
annotations?
j
Well it's a ~2000 module app. Migration is a big task. However, we have a 50-module codebase that targets JS (which runs inside our Android and iOS apps) where we use kotlin-inject. It was built from scratch in the last few years, so easier to use it there.
u
Yes but a given type is visible to both graphs? Or is there no overlap?
j
So there are types which are shared by both sides (we call them "guest" and "host"), but they're either values (pure data) or interfaces ("services"). No DI in either of those.
So for example we might have a
HttpClient
service interface which has
Request
and
Response
values as its function argument and return type. In the guest (JS) we'll bind the implementation of the
HttpClient
that calls out to the host into the kotlin-inject graph, and on the host (Android) the implementation of
HttpClient
will be in the graph and
@Binds
to
HttpClient
to be bound to the JS VM.
The JS VM sees one instance (a proxy implementation that calls out into the host) and the Android app has the real instance. Both are in their respective graphs which never touch, but they share the interface itself through Kotlin multiplatform so they agree on the API across this bridge.
u
but the implementation still lives in
commonMain
?
j
The interface and values live in commonMain
There is no DI in common
u
I'm a bit confused. Where does kotlin-inject live? Only in
jsMain
?
j
Yeah
u
Okay I see. Interesting. Btw do you see a way to migrate DI's incrementally? Or does it need to be atomic. Say the move from dagger to kotlin inject, where k-i would ideally live in commonMain. Other maybe a spin on your's, i.e. to hold the actual instance only in one graph, and then
@Provides
it to the other. Which would mean accessing the Components somehow statically in the @Provides function, but I guess would work (?)
Any chance you have insight about anvil's move to KMP? I read that latest memo about it turning to a pure compiler plugin & ditching dagger. Which obviously opens up road to KMP - but they said they don't have the use case..so..probably not?
j
I don't know anything about Anvil, no, sorry.
For migrating, I would probably do something like that. It gets tricky if the subgraph doesn't cleanly partition.
There are some nasty tricks where you could have both graphs have references to each other and providing instances from each other for that case. It would be messy, but temporary.
u
How? Do you mean to actually provide `AppComponent`instance into the other graph? Or is there some trick that will somehow autoregister all it's contents?
j
Yeah you provide the component of Dagger into the kotlin-inject, but into kotlin-inject you would have provided a placeholder for the Dagger component you then swap for the real thing once it gets built.
And then you do provider methods in each one which pull from the other graph.
It's ugly, but it means they can share ownership while you migrate things.
u
But I'd still need to "explode" it into the other graph right? Like this?
Copy code
@Module 
class DaggerModule {
   @Provides fun provideFoo(kiAppComponent): Foo {
      return kiAppComponent.foo
   }
}
j
yep
Although Dagger can actually automatically bind getter-style methods on an interface using component dependencies
u
Yea I'm a anvil user so I forgot how to do that 😄
j
so if you have
Copy code
interface Foo {
  getA(): A
  getB(): B
}
it can put A and B into the graph solely from a Foo
u
Btw I'm surprised cashapp doesn't use anvil. Especially in a 2000 module build? What does the top level
AppComponent
look like?
modules = [ .. ]
or 2000 module references? One of my main arguments against chosing
koin
over `kotlin-inject`was the necessity to plug a given gradle module's DI module manually
j
Heh, there are nowhere near to 2000 modules.
We use the module aggregation but not some other thing Anvil does
But we still also have like 20 or so normal modules
I don't pay much attention to it, honestly. I spend more time in the JS stuff and the whole system built around that.
u
//edited: Okay I actually read what you said, you do use anvil for the aggregation, sry
> I much prefer it as a Kotlin compiler plugin rather than being built on KSP Btw the compiler plugin, do you mean anvil?
j
No, kotlin-inject. It used to be a compiler plugin.
u
Oh, I wasn't aware. Btw how's the build times with k-i? I also read about issues with larger codebases, i.e. the top level component having 15k lines & compiler dying or something
j
The JS modules are small, relatively speaking. So we don't really notice (to my knowledge).