All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
- kotlin updated to 2.0.0
- Fixed various issues with qualifier annotations.
- Fixed issue with inner class or companion inside a component extending the component interface causing an error.
- Fixed crash when attempting to display a type parameter.
- Added ksp work-around when running multiple rounds to prevent a FileAlreadyExistsException on a duplicate symbol.
- Fixed injecting into a java constructor that doesn't have a nullability annotation defined.
- Fixed various issues when resolving cycles.
- Removed erroneous
@KmpComponentCreator
annotation.
@Scope
annotations now take arguments into account. This means for example, if you havethen the scope:@Scope annotation class NamedScope(val value: String)
@NamedScope("one")
and@NamedScope("two")
would be treated as distinct. Previously they were treated as the same scope.- Legacy implicit assisted injection (not using the
@Assisted
annotation) is now an error. - The build will now fail if multiple qualifiers are applied in the same place, instead of picking the first one. This
applies both to the new annotation (see below) and
javax.inject.Qualifier
. A minor exception is you are allowed to have one of each type to aid in migration. In that case theme.tatarka.inject
one will be chosen.
- Added a
me.tatarka.inject.annotations.Qualifier
annotation as an alternative to using typealias. For example, you could do:This behaves the same as@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" }
javax.inject.Qualifier
does when you haveme.tatarka.inject.enableJavaxAnnotations=true
. - Added a new
@KmpComponentCreate
annotation for nicer multiplatform support. This allows you to create component instances from common code when generating platform-specific outputs.see the new multiplatform docs for more details.// src/commonMain @Component abstract class MyKmpComponent @KmpComponentCreate expect fun createKmp(): MyKmpComponent
- You can now use an
@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 }
- The KAPT backend is removed, please migrate to KSP if you haven't already.
- Fixed cases of invalid code generation (#321, #337, #313).
- Fixed an exception thrown on KSP2 when running multiple rounds (google/ksp#1854).
- Fixed various issues with handling method overrides in components (#309, #375)
- Allow scope annotations on both an interface and component implementation if they are the same scope (#320).
- Fixed scoped assisted injection enforcement. It was incorrectly using the component scope instead of the scope of the assisted class or provides method.
- Updated kotlin to 1.9.0
- If a dependency's scope is not found on the component providing it, a better error message is given.
- Adding a
@Provides
annotation on an abstractfun
orval
will now warn that it has no effect. - When overriding a method the parent is checked to see if it has a
@Provides
annotation. This makes the example in the README actually work:@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"}
andmyStringItems
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.
- Fixed code generation issues with assisted injection.
-
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
- Fixed dependency resolution issue with native artifacts
-
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.
- You can now use function and
Lazy
types inSet
. This allows you to lazily construct its entries without having to change the type on your@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 = ... }
- Fixed issue with name collisions when generating arguments. This could cause invalid code to be generated as the inner arg would shadow the outer arg that was attempted to be used.
- Improved the error message for scoped provides in unscoped component.
- Fixed printing of an inner type to include the outer class. i.e. You will now get
Parent.Child
in error messages instead of justChild
which can make it easier to find the location of the error.
- Improved generated code formatting.
- Removed explicit retention annotation to get rid of kotlin js warnings.
- Fixes conflicting declarations when scoped
@Provides
functions returned the same type with different generic args. - Fixes default parameter handling with lambda or lazy values.
- Fixes to kotlin native implementation that should make it more usable across threads. Note: the new memory model limitation is still present, but you can use https://github.com/touchlab/Stately to wrap the access when using the legacy memory model.
- Updated kotlin to 1.5.31
- Updated ksp to 1.5.31-1.0.1
- Several improvements to code generation which often means less code is generated.
-
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")
- Updated kotlin to 1.6.0-RC
- Updated ksp to 1.6.0-RC-1.0.1-RC
- Several improvements to code generation which often means less code is generated.
- 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.
- Updated kotlin to 1.5.20
- Experimental kotlin js support
- Fix generated code for @Inject functions with a receiver ex:
@Inject fun Foo.bar() = ...
- Fix not using the typealias for function return types
- Updated kotlin to 1.5.10
- Updated ksp to beta01
- Fix metata parsing issue with kapt on kotlin 1.5.0
- Fix declaring function injection in another module in ksp
- Updated kotlin to 1.5.0
- Updated ksp to alpha10
-
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.
- Fix using
@Qualifier
on scoped dependencies - Fix declaring components as an inner class
- Fix annotating java class constructors with
@Inject
- Fix
@Inject
on a companion object
- Updated ksp to 1.4.30-1.0.0-alpha05
- Updated ksp to 1.4.30-1.0.0-alpha03
- Minimum supported kotlin version is now 1.4.30
-
Updated ksp to 1.4.20-dev-experimental-20210111
Key changes:
- You no longer have to define
resolutionStrategy
in yoursettings.gradle
. - The plugin id has changed from
symbol-processing
tocom.google.devtools.ksp
.
- You no longer have to define
-
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) { ... }
- Respect component's class visibility
- Fix generating incorrect code for fun providers
- Migrate ksp
- Improve kapt error messaging
- Build performance improvements
- Allow annotating interfaces with
@Component
- Support
javax.inject.Qualifer
- Fixed companion generation (
me.tatarka.inject.generateCompanionExtensions=true
) for ksp - Throw error when parent component is missing val instead of generating incorrect code.
- Initial Release