I'm trying to inject the viewmodel to the Fragment using hilt, for that I created two modules one for my network component and another for the characters viewmodel.
The network module is installed in SingletonComponent and I need that the characters module installs in the FragmentComponent to get the Viewmodel through "by viewmodels()"
My activity and my fragment are annotated with "#AndroidEntryPoint" and my Application is annotated with "#HiltAndroidApp" my modules are:
#Module
#InstallIn(SingletonComponent::class)
class NetworkModule {
#Singleton
#Provides
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
return loggingInterceptor
}
#Singleton
#Provides
fun provideAuthorizationInterceptor(): AuthorizationInterceptor =
AuthorizationInterceptor()
#Singleton
#Provides
fun provideInterceptors(
loggingInterceptor: HttpLoggingInterceptor,
authorizationInterceptor: AuthorizationInterceptor
): HttpInterceptors = HttpInterceptors(
listOf(
loggingInterceptor,
authorizationInterceptor
)
)
#Singleton
#Provides
fun provideTimeouts(): HttpTimeouts = HttpTimeouts()
#Singleton
#Provides
fun provideHttpClient(
timeouts: HttpTimeouts,
interceptors: HttpInterceptors
): HttpBuilderFactory = HttpClientBuilderFactory(timeouts, interceptors)
#Singleton
#Provides
fun provideGsonConverter(): GsonFactory = GsonBuilderFactory()
#Singleton
#Provides
fun provideHttpServiceProvider(
httpBuilderFactory: HttpBuilderFactory,
gsonFactory: GsonFactory
): HttpProvider = HttpServiceProvider(httpBuilderFactory, gsonFactory)
}
#Module
#InstallIn(FragmentComponent::class)
class CharacterListModule {
#Singleton
#Provides
fun provideCharacterMapper(): Mapper<CharacterDataContainer, PaginatedCharacters> =
PaginatedCharactersMapper()
#Singleton
#Provides
fun provideCharacterServices(
httpServiceProvider: HttpProvider
): CharacterServices = httpServiceProvider.createService(CharacterServices::class.java)
#Singleton
#Provides
fun provideCharacterListRemoteSource(
characterServices: CharacterServices,
characterMapper: Mapper<CharacterDataContainer, PaginatedCharacters>
): CharacterListSource = CharacterListRemoteSourceImpl(
characterServices,
characterMapper
)
#Singleton
#Provides
fun provideCharacterListRepository(
characterListRemoteSource: CharacterListSource
): CharacterListRepository = CharacterListRepositoryImpl(
characterListRemoteSource
)
#Singleton
#Provides
fun provideCoroutineDispatcher() = Dispatchers.IO
#Singleton
#Provides
fun provideCharacterListUseCase(
coroutineDispatcher: CoroutineDispatcher,
characterListRepository: CharacterListRepository
) = CharacterListUseCase(
coroutineDispatcher,
characterListRepository
)
#Singleton
#Provides
fun provideCharacterUIMapper(): Mapper<Character, CharacterUI> = CharacterUIMapper()
}
and my viewmodel is:
#HiltViewModel
class CharacterListViewModel #Inject constructor(
private val characterListUseCase: CharacterListUseCase,
private val characterUIMapper: Mapper<Character, CharacterUI>
) : ViewModel() {
Also when I use SingletonComponent in both cases the application runs fine, but when try to use FragmentComponent fails:
Finally my dependencies are:
object Hilt {
internal object Versions {
const val hilt = "2.33-beta"
const val hiltViewModel = "1.0.0-alpha01"
}
const val hilt = "com.google.dagger:hilt-android:${Versions.hilt}"
const val hiltCompiler = "com.google.dagger:hilt-android-compiler:${Versions.hilt}"
const val hiltViewModel = "androidx.hilt:hilt-lifecycle-viewmodel:${Versions.hiltViewModel}"
const val hiltViewModelCompiler = "androidx.hilt:hilt-compiler:${Versions.hiltViewModel}"
}
As it names says "FragmentComponent"(s) only live as long as it fragments does. You can't inject something that is fragment scoped into a view model, because viewmodels outlive its fragments lifecycle.. Change "FragmentScoped" with "SingletonScoped".
Please read the official documentation first before working with scopes. 99% of the time, using a "SingletonComponent" is more than enough
Related
I am trying two separate path collection query but hilt is throwing up a error of:
[Dagger/DuplicateBindings] com.google.firebase.firestore.CollectionReference is bound multiple times
How should i use two Collection Reference from my Repository to my AppModule?
#Module
#InstallIn(SingletonComponent::class)
object AppModule {
#Provides
fun provideFirebaseAuth(): FirebaseAuth = Firebase.auth
#Provides
fun provideFirebaseFirestore() = Firebase.firestore
#Provides
#Singleton
fun provideProductRef(db: FirebaseFirestore) = db.collection(Products)
#Provides
#Singleton
fun provideCategoriesRef(catDb: FirebaseFirestore) = catDb.collection(Categories)
#Provides
#Singleton
fun provideProductRepository(
productRef: CollectionReference,
cartRef: CollectionReference,
catRef: CollectionReference
): ProductRepository = ProductRepositoryImpl(productRef, cartRef,catRef)
#Provides
#Singleton
fun provideUseCases(productRepo: ProductRepository) = UseCases(
getProducts = GetProducts(productRepo),
addToCart = AddToCart(productRepo),
getCategories = GetCategories(productRepo)
)
My Repository
typealias Products = List<Product>
typealias Categories = List<Category>
typealias ProductResponse = Response<Products>
typealias CategoryResponse = Response<Categories>
typealias AddProductToCartResponse = Response<Boolean>
interface ProductRepository {
fun getProductsFromFirestore(): Flow<ProductResponse>
fun getCategoriesFromFirestore(): Flow<CategoryResponse>
suspend fun addProductToCartCollection(product: Product): AddProductToCartResponse
}
Error
/home/shiva/AndroidStudioProjects/ZuZu/app/build/generated/hilt/component_sources/debug/com/shivaconsulting/zuzu/ZuzuApp_HiltComponents.java:131: error: [Dagger/DuplicateBindings] com.google.firebase.firestore.CollectionReference is bound multiple times: It is also requested at:
com.shivaconsulting.zuzu.di.AppModule.provideProductRepository(productRef, …)
com.shivaconsulting.zuzu.di.AppModule.provideProductRepository(…, catRef)
You are getting the following error:
[Dagger/DuplicateBindings] com.google.firebase.firestore.CollectionReference is bound multiple times
Because of the presence of the following lines of code inside your AppModule class:
#Provides
#Singleton
fun provideProductRef(db: FirebaseFirestore) = db.collection(Products)
#Provides
#Singleton
fun provideCategoriesRef(catDb: FirebaseFirestore) = catDb.collection(Categories)
This means that you trying to create two objects of type CollectionReference, which is actually not possible because Hilt won't know which one to inject. To solve this, you have to differentiate them by naming them differently:
#Provides
#Singleton
#Named("products") //👈
fun provideProductsRef(db: FirebaseFirestore) = db.collection("products")
#Provides
#Singleton
#Named("categories") //👈
fun provideCategoriesRef(db: FirebaseFirestore) = db.collection("categories")
#Provides
#Singleton
#Named("cart") //👈
fun provideCartRef(db: FirebaseFirestore) = db.collection("cart")
Going forward, when you need to create a repository object you have to explicitly specify the names:
#Provides
#Singleton
fun provideProductRepository(
#Named("products") //👈
productsRef: CollectionReference,
#Named("categories") //👈
categoriesRef: CollectionReference,
#Named("cart") //👈
cartRef: CollectionReference
): ProductRepository = ProductRepositoryImpl(productsRef, categoriesRef, cartRef)
Later, when you want to use these three objects inside your repository implementation class you have to use:
#Singleton
class ProductRepositoryImpl #Inject constructor(
#Named("products") //👈
private val productsRef: CollectionReference,
#Named("categories") //👈
private val categoriesRef: CollectionReference,
#Named("cart") //👈
private val cartRef: CollectionReference
): ProductRepository {
//...
}
Base app architecture for Android client-server app is:
UI (Activity + Fragment or Jetpack Compose)
ViewModel (also is part of UI actually)
Data (repositories to get data from database (Room) and API (Retrofit))
Hilt has different Components and Scopes for Android https://dagger.dev/hilt/components.html
For my base app architecture I have set DI using Hilt in the following way:
Database
#InstallIn(SingletonComponent::class)
#Module
object DatabaseModule {
#Provides
#Singleton // only this one is marked as #Singleton to get the same instance all the time (logically for Database...)
fun provideDataBase(#ApplicationContext context: Context): AppDataBase {
return AppDataBase.getInstance(context)
}
#Provides
fun provideUsersDao(dataBase: AppDataBase): UsersDao = dataBase.getUsersDao()
}
Network (Datasource)
#InstallIn(SingletonComponent::class)
#Module
object NetworkModule {
#Provides
#Singleton // also as Room instance, it should return the same instance of http client
fun providesOkHttpClient(
httpLoggingInterceptor: HttpLoggingInterceptor,
...
): OkHttpClient = OkHttpClient.Builder()
.addInterceptor(headerInterceptor)
...
.build()
#Provides
#Singleton
fun provideRetrofit(
gson: Gson,
client: OkHttpClient,
queryConverterFactory: Converter.Factory
): Retrofit = Retrofit.Builder()
...
.client(client)
.build()
//... some other stuff needed to setup OkHttp and Retrofit
#Provides
fun provideUserApi(retrofit: Retrofit): UserApi = retrofit.create(UserApi::class.java)
#Provides
fun provideUserDataSource(
userApi: UserApi,
networkErrorConverterHelper: NetworkErrorConverterHelper
): UserDataSource = UserDataSourceImpl(userApi, networkErrorConverterHelper)
}
Repository
#InstallIn(SingletonComponent::class)
#Module
interface RepositoryModule {
#Binds
fun bindUserRepositoryImpl(impl: UserRepositoryImpl): UserRepository
}
class UserRepositoryImpl #Inject constructor(
private val userDataSource: UserDataSource,
private val userDbManager: UserDbManager
) : UserRepository {
override fun observeCachedUsers(): Flow<List<User>> ...
override fun fetchUsers(): Flow<Resource<Unit>> = ...
}
ViewModel
#HiltViewModel
class UsersViewModel #Inject constructor(
private val userRepository: UserRepository,
) : ViewModel() {
...
}
Activity
#AndroidEntryPoint
class UsersActivity : AppCompatActivity() {
val userViewModel: UserViewModel by viewModels()
...
}
So basically everywhere I have used SingletonComponent for Hilt modules and #Singleton annotation for some providers so it would return the same instance (of Database, Http client)
I'm trying to understand how good (correct) all of that and in which cases I could use other components/scopes based on this sample.
When app has only one activity and uses Fragments or Jetpack Compose to implement all screens then I guess I can use ActivityRetainedComponent instead of SingletonComponent for data modules, in this case Hilt releases all instances in memory when a user presses the back button to exit the app (activity is destroyed), in case of SingletonComponent these instances live until process of the app dies, so basically difference in this case is not big but still...
I have a problem with injection dagger into view model.The problem is that losing form inputs when rotating the screen.Is the problem in my injection or initialize my view model? Here is my viewmodel;
#HiltViewModel
class ProfilIslemViewModel #Inject constructor(application: Application,
private val kullaniciService: KullaniciService,
private val kullaniciDao:KullaniciDao
): AndroidViewModel(application), CoroutineScope {...}
And here is my fragment that use this view model;
#AndroidEntryPoint
class ProfilIslemFragment:Fragment(), ProfilIslemFragmentClickListener {
private val viewModel: ProfilIslemViewModel by viewModels();
...
}
Here is the module that injected class;
#Module
#InstallIn(SingletonComponent::class)
object KutuphanemAppModule {
#Singleton
#Provides
fun provideKutuphanemDatabase
(#ApplicationContext context:Context) = Room.databaseBuilder(
context,
KutuphanemDatabase::class.java,
KUTUPHANEM_DB_NAME
).build();
#Singleton
#Provides
fun provideRetrofit(client: OkHttpClient):Retrofit =
Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
#Singleton
#Provides
fun provideHttpClient(customSharedPreferences: CustomSharedPreferences):OkHttpClient{
return OkHttpClient.Builder().addInterceptor(object:Interceptor{
override fun intercept(chain: Interceptor.Chain): Response {
val request:Request = chain.request().newBuilder().addHeader("Authorization",
"Bearer "+customSharedPreferences.getStringFromSharedPreferences(APP_TOKEN_KEY).trim()).build();
return chain.proceed(request);
}
}).build();
}
#Singleton
#Provides
fun provideParametreDao(database: KutuphanemDatabase) = database.getParametreDao();
#Singleton
#Provides
fun provideParametreApi(retrofit: Retrofit):IParametreService = retrofit.create(IParametreService::class.java);
#Singleton
#Provides
fun provideKullaniciApi(retrofit: Retrofit):KullaniciService = retrofit.create(KullaniciService::class.java);
#Singleton
#Provides
fun provideKitapApi(retrofit: Retrofit):IKitapService = retrofit.create(IKitapService::class.java);
#Singleton
#Provides
fun provideKullaniciDao(database: KutuphanemDatabase) = database.getKullaniciDao();
}
When I rotate the screen the inputs are losing. How to solve this problem?
I found the error. It is not relevant my view model or injections. It caused by two way databinding. But even so I put this line into the contructor of viewmodel;
private val savedStateHandle: SavedStateHandle
I am controlling the state with this param.So my inputs are not losing.
I have 3 repositories:
interface MainRepository {
...
}
interface LocalRepository {
...
}
interface WebRepository {
...
}
Each Repository has it's own implementation:
#Singleton
class MainRepositoryImpl #Inject constructor(
private val localRepository: LocalRepository,
private val webRepository: WebRepository
) : MainRepository {
...
}
#Singleton
class LocalRepositoryImpl #Inject constructor(
private val localMapper: LocalMapper
private val popularMovieDao: PopularMovieDao
) : LocalRepository {
...
}
#Singleton
class WebRepositoryImpl #Inject constructor(
private val webMapper: WebMapper,
private val popularMovieApi: PopularMovieApi
) : WebRepository {
...
}
As you can see, MainRepository needs to have both other repositories injected into it, however,I can't really figure out how to do it.
Of course I can inject it with type of LocalRepositoryImpl or WebRepositoryImpl but I want to inject it with type of LocalRepository or WebRepository for more generalized approach.
Here is the module that I tried writing:
#InstallIn(ApplicationComponent::class)
#Module
object Module {
#Singleton
#Provides
fun provideWebRepository(): WebRepository {
return WebRepositoryImpl(mapper = WebMapper(), popularMovieApi = PopularMovieApi.getInstance())
}
#Singleton
#Provides
fun provideLocalRepository(): LocalRepository {
return LocalRepositoryImpl(mapper = LocalMapper(), // Here I can't really
// figure out how to get #Dao since it requires DB
// which requires context and etc
// which makes me think that I've got completely wrong approach to this)
}
}
My Module of LocalData:
#InstallIn(ApplicationComponent::class)
#Module
object LocalDataSourceModule {
#Singleton
#Provides
fun provideMainDatabase(#ApplicationContext context: Context): MainDatabase = MainDatabase.getInstance(context)
#Provides
fun providePopularMovieDao(mainDatabase: MainDatabase): PopularMovieDao = mainDatabase.popularMovieDao()
}
My Module of WebData:
#InstallIn(ApplicationComponent::class)
#Module
object RemoteDataSourceModule {
#Singleton
#Provides
fun providePopularMovieApi(): PopularMovieApi = PopularMovieApi.getInstance()
}
How do I properly Inject Implementations that I have (LocalRepositoryImpl & WebRepositoryImpl) while maintaining types of interfaces(LocalRepository & `WebRepository)??
Use #Binds. Instead of your object Module use following module:
#InstallIn(ApplicationComponent::class)
#Module
interface Module {
#Binds
fun bindWebRepository(repository: WebRepositoryImpl): WebRepository
#Binds
fun bindLocalRepository(repository: LocalRepositoryImpl): LocalRepository
}
It tells Dagger that if you need WebRepository dependency then it must provide WebRepositoryImpl and the same for LocalRepository and LocalRepositoryImpl.
What is the use case for #Binds vs #Provides annotation in Dagger2
Your Repositories
interface MainRepository {
...
}
interface LocalRepository {
...
}
interface WebRepository {
...
}
Implementation (No #Inject or #Singleton here!)
class MainRepositoryImpl constructor(
private val localRepository: LocalRepository,
private val webRepository: WebRepository
) : MainRepository {
...
}
class LocalRepositoryImpl constructor(
private val localMapper: LocalMapper
private val popularMovieDao: PopularMovieDao
) : LocalRepository {
...
}
class WebRepositoryImpl constructor(
private val webMapper: WebMapper,
private val popularMovieApi: PopularMovieApi
) : WebRepository {
...
}
Di.Module (Repository Module)
#Module
#InstallIn(ApplicationComponent::class)
object RepositoryModule {
#Singleton
#Provides
fun provideMainRepository(
localRepository: LocalRepository,
webRepository: WebRepository
): MainRepository = MainRepositoryImpl(localRepository, webRepository)
#Singleton
#Provides
fun provideLocalRepository(
localMapper: LocalMapper,
popularMovieDao: PopularMovieDao
): LocalRepository = LocalRepositoryImpl(localMapper, popularMovieDao)
#Singleton
#Provides
fun provideWebRepository(
webMapper: WebMapper,
popularMovieApi: PopularMovieApi
): WebRepository = WebRepositoryImpl(webMapper, popularMovieApi)
Try this and tell me if it worked. Since you have provided all your Repositories with #Provides, Dagger-Hilt knows how to create them. Are you using Room for your localDatabase? If yes, then creating the Database like you did might not be right. If you're not using Room that you should start to, as it makes your life easier.
Here is the right way to create a room database with dagger hilt:
Entity Module
#Entity(tableName = "exampleTableName")
data class ExampleEntity(
#PrimaryKey(autoGenerate = true)
val id: Int,
// ... whatever you need
val header: String = "",
val title: String = "",
val description: String = "",
)
ExampleDatabase
#Database(entities = [ExampleEntity::class], version = 1)
abstract class ExampleDatabase : RoomDatabase() {
abstract fun exampleDao(): ExampleDao
companion object {
const val DATABASE_NAME = "example_db"
}
}
DAO
#Dao
interface DocumentDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(exampleEntity: List<ExampleEntity>)
#Query("SELECT * FROM exampleTableName")
suspend fun getList(): List<ExampleEntity>
}
Room Di.Module
#Module
#InstallIn(ApplicationComponent::class)
object RoomModule {
#Singleton
#Provides
fun provideExampleDB(#ApplicationContext context: Context): ExampleDatabase = Room.databaseBuilder(
context,
ExampleDatabase::class.java,
ExampleDatabase.DATABASE_NAME,
).fallbackToDestructiveMigration().build()
#Singleton
#Provides
fun provideExampleDAO(exampleDatabase: ExampleDatabase): ExampleDao = exampleDatabase.exampleDao()
}
I've just refactored my dagger code to make it scalable and move all core stuff to a separate module called di.
Now when I try to inject my dependencies in the app module I got this :
[Dagger/MissingBinding] retrofit2.Retrofit cannot be provided without an #Inject constructor or an #Provides-annotated method.
public abstract interface ResetPasswordComponent {
^
retrofit2.Retrofit is injected at
com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordNetworkModule.providerResetPasswordAPI(retrofit)
com.sahra.oms.ibshop.data.remote.service.ResetPasswordService is injected at
com.sahra.oms.ibshop.data.repisotory.nationalid.UniqueRepositoryImpl(resetPasswordService)
com.sahra.oms.ibshop.data.repisotory.nationalid.UniqueRepositoryImpl is injected at
com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordModule.bindUniqueIdRepository(uniqueRepositoryImpl)
com.sahra.oms.ibshop.data.repisotory.nationalid.UniqueIdRepository is injected at
com.sahra.oms.ibshop.features.resetpassword.uniqueid.CheckUniqueIDViewModel(repository)
com.sahra.oms.ibshop.features.resetpassword.uniqueid.CheckUniqueIDViewModel is injected at
com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordModule.bindCheckIdViewModel(checkUniqueIDViewModel)
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.sahra.oms.ibishop.di.util.ViewModelFactory(viewModelsMap)
com.sahra.oms.ibishop.di.util.ViewModelFactory is injected at
com.sahra.oms.ibshop.di.ViewModelBuilder.bindViewModelFactory(arg0)
androidx.lifecycle.ViewModelProvider.Factory is injected at
com.sahra.oms.ibshop.features.resetpassword.newpassword.NewPasswordFragment.viewModelFactory
com.sahra.oms.ibshop.features.resetpassword.newpassword.NewPasswordFragment is injected at
com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordComponent.inject(com.sahra.oms.ibshop.features.resetpassword.newpassword.NewPasswordFragment)
ResetPasswordService is just a Retrofit interface.
Here is my code:
AppComponent
#Singleton
#AppScope
#Component
interface AppComponent {
fun provideContextComponent(): ContextComponent
fun provideNetworkComponent(): NetworkComponent
fun provideSharedPrefComponent(): SharedPreferencesComponent
fun inject(app: Application)
#Component.Factory
interface Factory {
fun create(
#BindsInstance
context: ContextComponent,
#BindsInstance
network: NetworkComponent,
#BindsInstance
sharedPrefs: SharedPreferencesComponent
): AppComponent
}
}
NetworkComponent :
#Scope
#Retention(AnnotationRetention.RUNTIME)
annotation class NetworkScope
#NetworkScope
#Component(
dependencies = [ContextComponent::class],
modules = [OkHttpModule::class, AuthBinderModule::class]
)
interface NetworkComponent {
fun provideOkHttp(): OkHttpClient
fun provideRetrofit(): Retrofit
fun provideGson(): GsonConverterFactory
}
OkHttpModule :
#Module
object OkHttpModule {
private const val BASE_URL = "base_url"
#Provides
#JvmStatic
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor(
HttpLoggingInterceptor.Logger { message -> Log.d("<<<network>>>", message) }).apply {
level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
}
}
#Provides
#JvmStatic
fun provideChuckInterceptor(app: Application): ChuckInterceptor = ChuckInterceptor(app)
#Provides
#JvmStatic
fun provideOkhttpCache(app: Application): Cache =
Cache(app.cacheDir, 50_000_000)
#Provides
#NetworkScope
#JvmStatic
fun provideClient(
loggingInterceptor: HttpLoggingInterceptor,
chuckInterceptor: ChuckInterceptor,
authInterceptor: Interceptor,
cache: Cache
): OkHttpClient {
return OkHttpClient.Builder()
.cache(cache)
.addInterceptor(loggingInterceptor)
.addInterceptor(authInterceptor)
.addInterceptor(chuckInterceptor)
.build()
}
#Provides
#NetworkScope
#JvmStatic
fun provideGson() = Gson()
#Provides
#JvmStatic
fun provideGsonConverter(gson: Gson) = GsonConverterFactory.create(gson)
#Provides
#NetworkScope
#JvmStatic
fun provideRetrofit(
gsonConverterFactory: GsonConverterFactory,
client: Lazy<OkHttpClient>
): Retrofit = Retrofit.Builder()
.callFactory { request -> client.get().newCall(request) }
.baseUrl(BASE_URL)
.addConverterFactory(gsonConverterFactory)
.build()
}
Here is how I try to inject the dependencies:
#FeatureScope
#Component(
dependencies = [AppComponent::class],
modules = [
ResetPasswordNetworkModule::class,
ResetPasswordModule::class,
ViewModelBuilder::class
]
)
interface ResetPasswordComponent {
fun inject(newPasswordFragment: NewPasswordFragment)
fun inject(checkUniqueIDFragment: CheckUniqueIDFragment)
#Component.Builder
interface Builder {
fun coreComponent(appComponent: AppComponent): Builder
fun build(): ResetPasswordComponent
}
}
#Module
abstract class ResetPasswordModule {
#Binds
abstract fun bindResetPasswordRepository(resetPasswordRepositoryImpl: ResetPasswordRepositoryImpl): ResetPasswordRepository
#Binds
abstract fun bindUniqueIdRepository(uniqueRepositoryImpl: UniqueRepositoryImpl): UniqueIdRepository
#Binds
#IntoMap
#ViewModelKey(CheckUniqueIDViewModel::class)
abstract fun bindCheckIdViewModel(checkUniqueIDViewModel: CheckUniqueIDViewModel): ViewModel
#Binds
#IntoMap
#ViewModelKey(NewPasswordViewModel::class)
abstract fun bindNewPasswordViewModel(newPasswordViewModel: NewPasswordViewModel): ViewModel
}
#Module
object ResetPasswordNetworkModule {
#Provides
#JvmStatic
#FeatureScope
fun provideUserAPI(
retrofit: Retrofit
): ResetPasswordService = retrofit.create(ResetPasswordService::class.java)
}
and here is my repository code:
class ResetPasswordRepositoryImpl #Inject constructor(
private val resetPasswordService: ResetPasswordService
) : ResetPasswordRepository {
}
Fragment :
class NewPasswordFragment{
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
}
class NewPasswordViewModel #Inject constructor(
private val repository: ResetPasswordRepository
)
Thanks in advance.
Your app component factory looks like this:
fun create(
#BindsInstance
context: ContextComponent,
#BindsInstance
network: NetworkComponent,
#BindsInstance
sharedPrefs: SharedPreferencesComponent
): AppComponent
This provides access to an instance of NetworkComponent, so any #Provides, #Binds, or #Inject that requires a NetworkComponent can get one. It does not, however, give direct access to NetworkComponent's object graph.
NetworkComponent already exposes Retrofit, so you can certainly get one if you have access to the component. However, this process is not automatic, and in your setting requires a #Provides method.
#Provides
fun provideRetrofit(component: NetworkComponent): Retrofit = component.provideRetrofit()
This is more convoluted that it needs to be. A better way to accomplish this is to make NetworkComponent a dependency of AppComponent (or just use its modules and drop the network component entirely), then expose Retrofit in AppComponent.
// using multiple scoped dependencies requires Dagger 2.27
#AppScope
#Component(dependencies = [ContextComponent::class, NetworkComponent::class, SharedPreferencesComponent::class])
interface AppComponent {
fun provideRetrofit(): Retrofit
// ...
}