How to solve missing InjectedFieldSignature Error implementing Dagger 2 in android? - android

I have implemented dependency injection in android before using dagger 2 but, recently, I have tried to use it in a new project but I get the following error:
error: cannot find symbol
import dagger.internal.InjectedFieldSignature;
^
symbol: class InjectedFieldSignature
location: package dagger.internal/location/to/App_MembersInjector.java:30: error: cannot find symbol
Here is my Application component:
#Singleton
#Component(
modules = [
(AndroidInjectionModule::class),
(VmModule::class),
(InjectorModule::class),
]
)
interface ApplicationComponent: AndroidInjector<Application> {
#Component.Builder
interface Builder{
#BindsInstance
fun application(application: App): Builder
fun build() : ApplicationComponent
}
fun inject(home: Home)
}
Then in my App class:
class App: Application(), HasAndroidInjector {
#Inject
lateinit var anAndroidInjector: DispatchingAndroidInjector<Any>
override fun onCreate() {
super.onCreate()
DaggerApplicationComponent.builder().application(this).build().inject(this)
}
override fun androidInjector(): AndroidInjector<Any> {
return anAndroidInjector
}
}
Then the injector module:
#Module
abstract class InjectorModule {
#ContributesAndroidInjector
abstract fun bindHomeActivity(): Home
}
The following is a small excerpt of my app Gradle to show the dagger version:
implementation 'com.google.dagger:dagger-android:2.24'
implementation 'com.google.dagger:dagger-android-support:2.24'
kapt 'com.google.dagger:dagger-android-processor:2.24'
kapt 'com.google.dagger:dagger-compiler:2.28'
If you have any clue, kindly let me know where the problem might be.

Your Dagger artifact versions don't match. Specifically, you are using dagger-compiler:2.28 to generate code, but including an dependency on Dagger 2.24 instead.
In the specific case of dagger.internal.InjectedFieldSignature, that class appears to have been introduced in Dagger version 2.25.3. Any later version of the Dagger compiler will expect that InjectedFieldSignature exists and can be used in generated code. However, since you're only including Dagger 2.24 in your project, the generated code ends up referring to a class that doesn't exist.
To fix this, make sure all of your Dagger dependencies use the same version.

Related

How to inject fragment with a view model factory for instrumentation testing in Android

I have the following Dagger component setup which works well for my app.
#Singleton
#Component(
modules = [
ApiModule::class,
DatabaseModule::class,
ViewModelModule::class,
ActivityModule::class,
AndroidSupportInjectionModule::class
]
)
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: Application): Builder
fun build(): AppComponent
}
}
I wish to provide a custom implementation for the ApiModule (Retrofit mock) and DatabaseModule (InMemory Room DB) for instrumentation testing. So I created a test component as below:
#Singleton
#Component(
modules = [
MockApiModule::class,
TestDatabaseModule::class,
ViewModelModule::class,
ActivityModule::class,
AndroidSupportInjectionModule::class
]
)
interface TestComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
fun build(): TestComponent
}
}
My fragment requires an injection of ViewModelProvider.Factory
class MovieListingFragment : BaseFragment() {
#Inject lateinit var factory: ViewModelProvider.Factory
private val vm: MovieViewModel by navGraphViewModels(R.id.mobile_navigation) { factory }
which I'm generating using the ViewModelModule as below:
#Module
interface ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(MovieViewModel::class)
fun bindMovieViewModel(vm: MovieViewModel): ViewModel
#Binds
fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
How do I inject this in the fragment under test?
#RunWith(AndroidJUnit4::class)
class MovieListFragmentTest {
#Test
fun testMovieListFragment() {
val scenario = launchFragmentInContainer<MovieListingFragment>()
scenario.moveToState(Lifecycle.State.RESUMED)
}
}
There are a bunch of ways to approach this but the approach that worked best for me was to create a special mock buildType with a different dependency injection setup for the APIs where I didn't want to use the "real thing". My UI tests would then be under src/androidTestMock so that it runs the mock buildType.
You'll also have to:
Interface your APIs so you can provide mocks or real implementations for your build types
Implement your non-mocked DI setup not in your main sources but in each of your build types (e.g. debug, release). This only applies for the APIs that need special versions in your mock build type. Modules that are the "real thing" across all build types go into your main sources. This will lead to some code duplication that, if you really want to, you can avoid by having a non-mock source set.
Make sure your mocked classes are provided as singletons or your test (which will be doing the mocking or the different test setup) will receive a different instance than your app.
Make sure to CI/CD your mock built type too since you will mostly be working with other build types and you'll want catch a broken compile early.
In your case you'd have the TestComponent in src/mock, and AppComponent in src/debug and src/release.

Runtime JAR files in the classpath should have the same version

I have a project which I use Dagger2 & kotlin, I imported "org.jetbrains.kotlin:kotlin-stdlib:1.3.50” in my build.gradle and I was getting this Error:
w: Runtime JAR files in the classpath should have the same version.
These files were found in the classpath:
w:
/Users/macbook/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jre7/1.1.51/8b5933578dc55f32cfc1a25f1db6371e4161fb8f/kotlin-stdlib-jre7-1.1.51.jar:
kotlin-stdlib-jre7 is deprecated. Please use kotlin-stdlib-jdk7
instead
So therefore I changed from "kotlin-stdlib" to "kotlin-stdlib-jdk7" using:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50” and I am still getting this error.
I then made some research and found this URL: warning: Kotlin runtime JAR files in the classpath should have the same version
I found the usage of kotlin-reflect: "implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.50"
I added it to my build.gradle BUT I still have the same errors
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7: 1.3.50"
implementation "org.jetbrains.kotlin:kotlin-reflect: 1.3.50"
This is my BaseApplication class below, which I am calling the Dagger components :
class BaseApplication : Application() {
companion object {
#JvmStatic lateinit var netComponent: NetComponent
#JvmStatic lateinit var exampleComponent: ExampleComponent
}
override fun onCreate() {
super.onCreate()
netComponent = DaggerNetComponent.builder()
.appModule(AppModule(this))
.netModule(NetModule())
.build()
exampleComponent = DaggerExampleComponent.builder()
.netComponent(netComponent)
.retrofitModule(RetrofitModule())
.exampleModule(ExampleModule(this))
.build()
}
}

How to Add Generated Dagger Module to a Component

I am currently building a library that generates interactors so lets say this is the interactor interface
interface DeleteProductUsecase :EitherInteractor<None , None, Failure.SubmitionFailure>
This interface will be annotated with this annotation:
#WorkiUsecase(ProductRepository::class)
With annotation processing, I'm generating a class that implement the interface like this:
class Generated_AddProductUsecase #Inject constructor(couroutineDispatchers: CouroutineDispatchers, val repo: ProductRepository) : DeleteProductUseCase
And a module that will provide our interactor:
#Module abstract class WorkiModule {
#Binds
abstract fun provideGenerated_AddProductUsecase(): AddProductUsecase
}
so the problem is I need to access the module to add it to the appComponent but in compile time the module generated is not yet generated so I can't access. it

Multi module project: how to setup Dagger to provide interface but hide implementation-specific dependencies?

In my application, I have two modules: app and repository.
repository depends on Room, and has a GoalRepository interface:
interface GoalRepository
and a GoalRepositoryImpl class that is internal, as I don't want to expose it or the Room dependency to other modules:
#Singleton
internal class GoalRepositoryImpl #Inject constructor(private val dao: GoalDao) : GoalRepository
app depends on repository to get a GoalRepository instance.
I have a GoalRepositoryModule that, at the moment, is:
#Module
class GoalRepositoryModule {
#Provides
#Singleton
fun provideRepository(impl: GoalRepositoryImpl): GoalRepository = impl
#Provides
#Singleton
internal fun provideGoalDao(appDatabase: AppDatabase): GoalDao = appDatabase.goalDao()
#Provides
#Singleton
internal fun provideDatabase(context: Context): AppDatabase =
Room.databaseBuilder(context, AppDatabase::class.java, "inprogress-db").build()
}
The issue is that this won't compile (obviously) as the public provideRepository function is exposing GoalRepositoryImpl, that is an internal class.
How can I structure my Dagger setup to achieve what I want?
Edit:
I tried making provideRepository internal as per #David Medenjak comment and now the Kotlin compiler complains that it cannot resolve RoomDatabase dependency:
Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
class xxx.repository.database.AppDatabase, unresolved supertypes: androidx.room.RoomDatabase
For completeness, the code of my Component inside the app module:
#Component(modules = [ContextModule::class, GoalRepositoryModule::class])
#Singleton
interface SingletonComponent
After looking at the code Dagger was generating, I understood that the mistake was making the #Component inside the app module depend on the #Module inside the repository module.
So I made a separate #Component inside the repository module and made the app module's one depend on it.
The code
The repository module's component:
#Component(modules = [GoalRepositoryModule::class])
interface RepositoryComponent {
fun goalRepository(): GoalRepository
}
The app's one:
#Component(modules = [ContextModule::class], dependencies = [RepositoryComponent::class])
#Singleton
interface SingletonComponent
This way the RepositoryComponent is responsible for building the Repository and knows all its dependencies, while the SingletonComponent only have to know about RepositoryComponent.

Dagger 2 androidx fragment incompatible types

I'm using Dagger 2.21 and when I try to do
#Module
internal abstract class FragmentModule {
#ContributesAndroidInjector
internal abstract fun loginFragment() : LoginFragment
}
and
#Singleton
#Component(modules = [AndroidSupportInjectionModule::class, AppModule::class, ActivityModule::class, ViewModelBuilder::class, ViewModelModule::class, RepositoriesModule::class, ApiModule::class, FragmentModule::class])
interface AppComponent : AndroidInjector<PhotocoApplication> {
#Component.Builder
abstract class Builder : AndroidInjector.Builder<PhotocoApplication>()
}
I get this error:
/app/build/generated/source/kapt/debug/com/photoco/app/injection/module/FragmentModule_LoginFragment$app_debug.java:18: error: incompatible types: Class LoginFragment cannot be converted to Class extends Fragment
I have been searching and saw that using 2.21 and setting this gets it to work but no luck yet
android.useAndroidX=true ; android.enableJetifier=true
LoginFragment extends:
dagger.android.support.DaggerFragment()
With all this setup can't get it to build, am I missing something here? I can make it work with Activities using DaggerActivity but not with Fragments.
PhotocoApplication extends dagger.android.support.DaggerApplication
Thanks!
Fixed this issue by updating all dagger dependencies to 2.21, was missing android-support (was still using 2.16).
implementation 'com.google.dagger:dagger:2.21'
implementation 'com.google.dagger:dagger-android:2.21'
implementation 'com.google.dagger:dagger-android-support:2.21'
kapt "com.google.dagger:dagger-compiler:2.21"
kapt "com.google.dagger:dagger-android-processor:2.21"
I just had the same problem but with two non-Android classes: EventBus and a wrapper class around Android resources.
I tried the solution proposed by Emanuel Amiguinho even it had nothing to do with android-support and it got fixed. So I tried to remove the added dependency and retry, and magically built successfully again.
So I guess in my case it was some caching issue.

Categories

Resources