Dependency injection lib for kotlin
APACHE-2.0 License
Bot releases are visible (Hide)
@KmpComponentCreator
annotation.Published by evant 4 months ago
@Scope
annotations now take arguments into account. This means for example, if you have
@Scope
annotation class NamedScope(val value: String)
then the scope: @NamedScope("one")
and @NamedScope("two")
would be treated as distinct. Previously they were@Assisted
annotation) is now an error.javax.inject.Qualifier
. A minor exception is you are allowed tome.tatarka.inject
one will be chosen.me.tatarka.inject.annotations.Qualifier
annotation as an alternative to using typealias. For example, you@Qualifier
annotation class Named(val value: String)
@Inject
class MyClass(@Named("one") val one: String, @Named("two") val two: String)
@Component
abstract class MyComponent {
abstract val myClass: MyClass
@Provides @Named("one")
fun provideOne(): String = "one"
@Provides @Named("two")
fun provideTwo(): String = "two"
}
This behaves the same as javax.inject.Qualifier
does when you have me.tatarka.inject.enableJavaxAnnotations=true
.@KmpComponentCreate
annotation for nicer multiplatform support. This allows you to create component// src/commonMain
@Component
abstract class MyKmpComponent
@KmpComponentCreate
expect fun createKmp(): MyKmpComponent
see the new multiplatform docs for more details.@Inject
annotation on an inner class provided the outer class can be provided.
@Inject class Outer { @Inject inner class Inner }
@Component abstract class MyComponent { abstract val inner: Outer.Inner }
Published by evant about 1 year ago
Published by evant about 1 year ago
@Provides
annotation on an abstract fun
or val
will now warn that it has no effect.@Provides
annotation. This makes the example in@NetworkScope abstract class NetworkComponent {
@NetworkScope @Provides abstract fun api(): Api
}
@Component abstract class RealNetworkComponent : NetworkComponent() {
// This is now treated as a @Provides even if not annotated directly
override fun api(): Api = RealApi()
}
Typealiases are treated as separate types in multibinding. This is consistent with other
uses of typealiases.
For example:
typealias MyString = String
@Component abstract class MyComponent {
abstract val stringItems: Set<String>
abstract val myStringItems: Set<MyString>
@Provides @IntoSet fun stringValue1(): String = "string"
@Provides @IntoSet fun stringValue2(): MyString = "myString"
}
stringItems
will contain {"string"}
and myStringItems
will contain {"myString"}
.
Lambda types now work in set multibindings.
@Component abstract class MyComponent {
abstract val lambdaSet: Set<() -> String>
@Provides @IntoSet fun lambda1(): () -> String = { "one" }
@Provides @IntoSet fun lambda2(): () -> String = { "two" }
}
Assisted injection no longer works with scopes or cycles. These two cases would over-cache the instance, ignoring the
assisted arguments. They now throw an error instead.
// now throws cycle error when providing
@Inject class AssistedCycle(val factory: (Int) -> AssistedCycle, @Assisted val arg: Int)
// now throws error when providing
@MyScope @Inject class AssistedScoped(@Assisted val arg: Int)
Fixed edge case where accessing a parent scoped dependency from a lazy cycle generated invalid code.
Published by evant over 1 year ago
Published by evant over 1 year ago
Added the ability to explicitly mark assisted injection parameters with an @Assisted
annotation. Not providing them
will currently warn which will become an error in the future. This allows better documentation on which params are
injected and which ones are provided by the caller. It also allows more flexibility for parameter ordering, you can
put the assisted params at the start instead of at the end if you so choose.
For example, if you have:
@Inject class AssistedClass(arg1: One , arg2: Two, arg3: Three)
@Inject Usage(createAssistedClass: (Two, Three) -> AssistedClass)
you should update it to:
@Inject class AssistedClass(arg1: One , @Assisted arg2: Two, @Assisted arg3: Three)
@Inject
annotations being ignored if used through a typealias, ex:
typealias MyInject = Inject
@MyInject class MyClassToInject
Published by evant about 2 years ago
Published by evant about 2 years ago
The kapt backend is now deprecated and will be removed in a future release. Please migrate to ksp instead.
Introduced some stricter checks to catch issues with certain graph setups. This may cause graphs that compiled before
to no longer compile. Specifically:
@MyScope @Component abstract class ParentComponent
@MyScope @Component abstract class ChildComponent(@Component val parent: ParentComponent)
will fail with:
Cannot apply scope: @MyScope ChildComponent
as scope @MyScope is already applied to parent ParentComponent
as it's ambiguous what the lifetime of the given scope should be. And:
@Component abstract class ParentComponent {
@Provides fun foo(bar: Bar): Foo = ...
}
@Component abstract class ChildComponent(@Component val parent: ParentComponent) {
abstract val foo: Foo
@Provides fun bar(): Bar = ...
}
will fail with:
Cannot find an @Inject constructor or provider for: Bar
In other words a parent component can no longer depend on a dependency provided by a child component. Not only does
this lead to confusing graphs, but it can lead to memory leaks if the parent component lives longer than the child and
ends holding on to that child dependency.
Lazy
types in Set
. This allows you to lazily construct its entries without having to@IntoSet
methods.
@Component abstract class MyComponent {
val funSet: Set<() -> Foo>
val lazySet: Set<Lazy<Foo>>
@Provides @IntoSet fun foo1(): Foo = ...
@Provides @IntoSet fun foo2(): Foo = ...
}
Parent.Child
in error messagesChild
which can make it easier to find the location of the error.Published by evant almost 3 years ago
@Provides
functions returned the same type with different generic args.Published by evant almost 3 years ago
Multiple rounds handling: This includes support for using types generated by other ksp processors. As a side effect
there is better error reporting for unresolved types.
Support for multiplatform/native. Check out the sample project.
Note: components are thread-safe, however you will run into issues actually using them from other threads unless you
enable the new memory model.
Added support for default args when injecting. If the type is present in the graph, it'll be injected, otherwise the
default will be used.
@Inject class MyClass(val dep: Dep = Dep("default"))
@Component abstract ComponentWithDep {
abstract val myClass: MyClass
@Provides fun dep(): Dep = Dep("injected")
}
@Component abstract ComponentWithoutDep {
abstract val myClass: MyClass
}
ComponentWithDep::class.create().myClass.dep // Dep("injected")
ComponentWithoutDep::class.create().myClass.dep // Dep("default")
Published by evant almost 3 years ago
Published by evant over 3 years ago
@Inject fun Foo.bar() = ...
Published by evant over 3 years ago
Published by evant over 3 years ago
Published by evant over 3 years ago
Allow cycles when there is delayed construction
You can now break cycles by using Lazy
or a function. For example,
@Inject class Foo(bar: Bar)
@Inject class Bar(foo: Foo)
will fail with a cycle error, but you can fix it by doing
@Inject class Foo(bar: Lazy<Bar>)
@Inject class Bar(foo: Foo)
or
@Inject class Foo(bar: () -> Bar)
@Inject class Bar(foo: Foo)
This uses lateinit
under the hood. You will get a runtime exception if you try to use the dependency before
construction completes.
Added option me.tatarka.inject.dumpGraph
to print the dependency graph while building. This can be useful for
debugging issues.
Allow type-alias usage with @IntoMap
.
You can now do
typealias Entry = Pair<String, MyValue>
@Component {
@Provides @IntoMap
fun entry1(): Entry = "1" to MyValue(1)
@Provides @IntoMap
fun entry2(): Entry = "2" to MyValue(2)
}
Code-gen optimization to reduce code size
ksp performance improvements
Made handling of nullable and platform types consistent on the different backends.
It is now an error to return a platform type from a @Provides
methods, you must declare the return type explicitly.
@Qualifier
on scoped dependencies@Inject
@Inject
on a companion objectPublished by evant over 3 years ago
Published by evant over 3 years ago
Published by evant almost 4 years ago
Updated ksp to 1.4.20-dev-experimental-20210111.
Key changes:
resolutionStrategy
in your settings.gradle
.symbol-processing
to com.google.devtools.ksp
.Minimum supported kotlin version is now 1.4.20
Support injecting suspend functions
You can now define suspend
component and provides methods
@Component abstract class MyComponent {
abstract val foo: suspend () -> IFoo
val providesFoo: suspend () -> IFoo
@Provides get() = { Foo() }
}
Support default args in component constructors
If you define any default args, you will get an overload create
function that provides default values. Due to
processor limitations, you only get a single overload (i.e. you cannot pass defaults from some args but not other).
This can be useful for more conveniently defining parent components.
@Component abstract class MyComponent(@Component val parent: ParentComponent = ParentComponent()) {
...
}
val component = MyComponent::class.create()
Support annotating constructors with @Inject
Sometimes you don't want to use the primary constructor to construct an object. You can now annotate a more specific
constructor instead.
class MyClass {
@Inject constructor(arg: String) // use this one for injection
constructor(arg: Int)
}
Support injecting objects
While you can use an object directly, it may be useful to inject it so that you can switch it to an instance at a
later point without updating the consuming code.
@Inject object Foo { ... }
@Inject MyClass(dep: Foo) { ... }
@Component
javax.inject.Qualifer
me.tatarka.inject.generateCompanionExtensions=true
) for ksp