DaggerHilt Multi Modular Project Cannot access class issue - android

I have created a Multi-Modular project in android with dagger implementation. my sub module structure for data module is below.
When ever I try to access method of my NewsApiInterface from NewsDetailsImpl inside testRepo() method and on compile time I get the error
Cannot access class 'retrofit2.Response'. Check your module classpath
for missing or conflicting dependencies
Module:
data (Module)
repository (Implementation Module)
network(API Service Module)
cache (Database Module)
repository Module -> datasource
class NewsDetailsImpl #Inject constructor(
val db: NewsDatabase,
val service: NewsApiInterface
) : NewsDetailsRepository {
override suspend fun fetchNewsDetail(): Flow<News> = flow{
}
override suspend fun fetchNewsList(
query: String,
limitStart: String,
limitEnd: String
): Flow<List<News>> = flow {
}
fun testRepo(query: String){
val news = service.getNews(query)
}
}
repository module -> di
#Module
#InstallIn(SingletonComponent::class)
class RepositoryModule {
#Singleton
#Provides
fun provideNewsRepository( db: NewsDatabase,
service: NewsApiInterface
): NewsDetailsRepository {
return NewsDetailsImpl(db,service)
}
}
data module -> network module -> di
#Module
#InstallIn(SingletonComponent::class)
object NetworkModule {
#Provides
#Singleton
fun providesNewsService() : NewsApiInterface {
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(
OkHttpClient.Builder()
.addInterceptor { chain ->
val url = chain
.request()
.url
.newBuilder()
.addQueryParameter("apiKey", NetworkConstants.API_KEY)
.build()
chain.proceed(chain.request().newBuilder().url(url).build())
}
.build()
)
.baseUrl(BASE_URL)
.build()
return retrofit.create(NewsApiInterface::class.java)
}
}
data module -> network module
interface NewsApiInterface {
#GET("everything")
public fun getNews(#Query("q") query: String): Response<NewsDto>
}

Related

dagger2 is throwing an error: cannot be provided without an #Provides-annotated method. in my android project's build

Hello folks please help me to debug this dagger2 issue,I am very new to dagger2 and just started with a project.
public abstract interface ApplicationComponent {
^
java.util.Map<java.lang.Class<?
>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<?>>> is injected at
dagger.android.DispatchingAndroidInjector(injectorFactoriesWithClassKeys, �)
dagger.android.DispatchingAndroidInjector<java.lang.Object> is injected at
com.example.dictionary.App.dispatchingServiceInjector
com.example.dictionary.App is injected at
com.example.dictionary.di.components.ApplicationComponent.inject(com.example.dictionary.App)
error: [Dagger/MissingBinding]
java.util.Map<java.lang.String,javax.inject.Provider<dagger.android.AndroidInjector.Factory<?
>>> cannot be provided without an #Provides-annotated method.
My implementations are:
interface ApiService {
companion object {
const val BASE_URL= "https://od-api.oxforddictionaries.com/"
}
#GET("api/v2/entries/en-gb/")
fun getMeaning(#Query("word") word: String): Observable<Response>
}
}
open class RemoteDataSource #Inject constructor(private val apiService: ApiService) {
fun getWordMeaning(word: String) = apiService.getMeaning(word)
}
#ApplicationScope
#Component(modules = [ApplicationModule::class, NetworkModule::class])
interface ApplicationComponent {
fun inject(app: App)
fun inject(mainActivity: MainActivity)
}
#Module
class ApplicationModule(private val application: Application) {
#ApplicationScope
#Provides
fun provideApplicationContext(): Context = application.applicationContext
#ApplicationScope
#Provides
fun provideSharedPreferences(): SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(application)
#ApplicationScope
#Provides
fun providerDisplayMetrics(): DisplayMetrics = application.resources.displayMetrics
}
#Module
class NetworkModule(
private val baseUrl: String,
private val application: Application
) {
companion object {
const val CACHE_SIZE = 10 * 1024 * 1024L // 10 MiB
const val TIME_OUT = 10L // time in minutes to get the response from server
}
#ApplicationScope
#Provides
fun provideGson(): Gson = GsonBuilder().create()
#ApplicationScope
#Provides
fun provideOkHttpCache() = Cache(application.cacheDir, CACHE_SIZE)
#ApplicationScope
#Provides
fun provideOkHttpClient(cache: Cache): OkHttpClient = with(OkHttpClient.Builder()) {
writeTimeout(3, TimeUnit.MINUTES)
.connectTimeout(3, TimeUnit.MINUTES)
.readTimeout(TIME_OUT, TimeUnit.MINUTES)
cache(cache)
addInterceptor(headersInterceptor())
build()
}
#ApplicationScope
#Provides
fun provideRetrofit(gson: Gson, okHttpClient: OkHttpClient): Retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
#ApplicationScope
#Provides
fun provideApiService(retrofit: Retrofit): ApiService =
retrofit.create(ApiService::class.java)
private var authRequest: String? = null
private fun headersInterceptor() = Interceptor { chain ->
val requestBuilder = chain.request().newBuilder()
requestBuilder.addHeader("Accept", "application/json")
requestBuilder.addHeader("app_id", "SOME APP ID")
requestBuilder.addHeader("app_key", "Some API KEY")
chain.proceed(
requestBuilder.build()
)
}
}
class App : Application(), HasAndroidInjector {
#Inject
lateinit var dispatchingServiceInjector:
DispatchingAndroidInjector<Any>
override fun onCreate() {
super.onCreate()
applicationComponent.inject(this)
RxJavaPlugins.setErrorHandler {
it.printStackTrace()
}
}
override fun androidInjector(): AndroidInjector<Any> {
return dispatchingServiceInjector
}
val applicationComponent: ApplicationComponent by
lazy<ApplicationComponent>(mode = LazyThreadSafetyMode.NONE) {
DaggerApplicationComponent
.builder()
.applicationModule(ApplicationModule(this))
.networkModule(NetworkModule(ApiService.BASE_URL, this))
.build()
}
}
I tried re-building the code, also attempted invalidate cache and restart to re-install dependencies, as dagger2 requires re-building the project every time once a new dependency injection is implemented, followed YouTube tutorials, to implement this so far, but unable to detect the problem, although in the build crash its indicating to provide #Provides, but in my NetworkModule, ApplicationModule I set this #Provides annotation.
Please help to figure out the solution.
As suggested in this answer, just add your App class in the android manifest under the application tag.
<application ...
android:name = ".App" ...>
...

Retrofit APIService Injection by Dagger Hilt in MVP Architecture's Model Class

I am trying to inject retrofit APIServices dependency into the model class. Here is My API Module Source Code:
#Module
#InstallIn(SingletonComponent::class)
object ApiModule {
#Singleton
#Provides
fun providesHttpLoggingInterceptor() = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
#Singleton
#Provides
fun providesOkHttpClient(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient =
OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.build()
#Singleton
#Provides
fun providesRetrofit(okHttpClient: OkHttpClient): Retrofit =
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(ApiConfig.BASE_URL)
.client(okHttpClient)
.build()
#Singleton
#Provides
#Named("ApiService")
fun providesApiService(retrofit: Retrofit):ApiServices =
retrofit.create(ApiServices::class.java)
}
For User Registration, I am using MVP Architecture Pattern where FragmentRegistration.kt is view layer, RegistrationModel is model layer class
When I inject ApiServices dependency into FragmentRegistration, it works fine. But when I try to inject it into model layer class, which is RegistrationModel, It doesn't work.
RegistrationModel:
class RegistrationModel(
val presenter: RegistrationContract.Presenter
) : RegistrationContract.Model {
#Inject
#Named("ApiService")
lateinit var apiServices: ApiServices
override fun onDataReady(registrationData: RegistrationData) {
val map = mapOf(
"Accept" to "application/json",
"Content-Type" to "application/json"
)
apiServices.userRegistration(map, registrationData)
.enqueue(object : Callback<RegistrationResponse> {
override fun onResponse(
call: Call<RegistrationResponse>,
response: Response<RegistrationResponse>
) {
if (response.isSuccessful) {
Log.d(TAG, "onDataReady: ${response.body().toString()}")
} else {
val apiFailure = APIFailure(
response.code(),
response.message()
)
presenter.onSignupFailure(apiFailure)
Log.d(TAG, "onDataReady: Error ${response.code()}")
Log.d(TAG, "onDataReady: Error Body ${response.errorBody()}")
}
}
override fun onFailure(call: Call<RegistrationResponse>, t: Throwable) {
presenter.onSignupFailure(
APIFailure(-1, t.toString())
)
Log.d(TAG, "onFailure: $t")
}
})
}
companion object {
const val TAG = "RegistrationModel"
}
}
In the above's Code,
#Inject
#Named("ApiService")
lateinit var apiServices: ApiServices
this dependency injection is not working.
You are trying to inject a filed provided by Hilt into a class which is not managed by Hilt. This will not work out of the box. You have to define EntryPoint for you custom class, so the Hilt can perform injection. You can read how to do that here: https://developer.android.com/training/dependency-injection/hilt-android#not-supported

Android inject firebase firestore with hilt in repository pattern

I have problem with my project when try add firebaseFirestore in constructor of my repository, "error: cannot find symbol
return DaggerBraziliexApplication_HiltComponents_SingletonC.builder()"
and with that I can't build the project, how should I proceed with the injection in this case?
Interface
interface NewsRepository {
suspend fun fetchNewsFromApi(
query: String,
date: String,
apiKey: String
): Flow<Result<List<NewsModelView>>>
}
RepositoryImpl
class NewsRepositoryImpl #Inject constructor(
private val apiService: NewsService,
private val newsMapper: NewsMapper,
private val firestore: FirebaseFirestore
): NewsRepository {
override suspend fun fetchNewsFromApi(
query: String,
date: String,
apiKey: String
): Flow<Result<List<NewsModelView>>> {
//TODO Implement API Request
}
private fun saveDataInFirestore(modelView: List<NewsModelView>, date: String) {
//TODO use firebaseFirestore do save response
}
}
Hilt Module:
#Module
#InstallIn(SingletonComponent::class)
object NetworkModule {
private const val baseUrl = "https://newsapi.org/v2/"
#Provides
#Singleton
#Named("news_api")
fun provideRetrofit(): Retrofit{
return Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
#Provides
#Singleton
fun provideNewsService(#Named("news_api") retrofit: Retrofit): NewsService{
return retrofit.create(NewsService::class.java)
}
#Provides
#Singleton
fun provideFirestore() = FirebaseFirestore.getInstance()
}
#Module
#InstallIn(ViewModelComponent::class)
abstract class RemoteModule {
#Binds
#ViewModelScoped
abstract fun bindRemoteRepository(remoteRepository: NewsRepositoryImpl): NewsRepository
}

Clean Architecture in Android using Dagger

I decided to add clean architecture to my project, I added three separate modules to the project: domain, data, presentation (app) and I divided the code into thees three modules. After that, I had a problem with Dagger, when I try to build the application, it says that it cannot access the WeatherDataApiService (this is the interface in which I make an API request using the retrofit library) I transferred this interface to the data module. In general, the problem is that I do not understand how to properly organize dependency injection so that classes have access to each other. The problem is that when Dagger is being built, access to the data module is closed. The problem is that I need to correctly distribute the dependencies between the modules. At the moment, my dependencies between the modules are built in this way - presentation(app) depends on the domain module, and the domain module depends on the data module
This is the interface WeatherDataApiService
interface WeatherDataApiService {
#GET("/v2.0/forecast/daily")
fun getWeatherData(
#Query("city") city: String,
#Query("days") days: Int,
#Query("units") degreeType: String
):Single<WeatherDataApiModel>
companion object {
operator fun invoke(): WeatherDataApiService {
val key = "40a7956799be42f49bc8b6ac4bb8e432"
val requestInterceptor = Interceptor{chain->
val url = chain.request()
.url() // HttpUrl
.newBuilder() // HttpUrl.Builder
.addQueryParameter("key", key) // HttpUrl.Builder
.build()
val request = chain.request()
.newBuilder() // Request.Builder
.url(url) // Request.Builder
.build() // Request
return#Interceptor chain.proceed(request) // Response
}
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(requestInterceptor) // OkHttpClient.Builder()
.build()
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://api.weatherbit.io")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build()
.create(WeatherDataApiService::class.java)
}
}
}
This is the repository located in the domain module.
class WeatherDataRepositoryImpl #Inject constructor(
private val weatherDataApiService : WeatherDataApiService,
private val mapper : WeatherDataMapper
) : WeatherDataRepository {
override fun getWeatherData(
city: String,
days: Int,
degreeType: String
): Single<WeatherData> =
weatherDataApiService.getWeatherData(city, days, degreeType)
.map { mapper.mapWeather(it) }
}
This is the use case code where I use the repository
class FetchWeatherDataUseCase #Inject constructor(private val weatherDataRepository: WeatherDataRepository) {
fun fetchWeatherData(city: String,days: Int,degreeType: String): Single<WeatherData> {
return weatherDataRepository.getWeatherData(city,days,degreeType)
}
}
This is the code of the Dagger module which is in domain
#Module
class WeatherDataRepositoryModule {
#Provides
#Singleton
fun providerWeatherDataRepository(
mapper: WeatherDataMapper,
weatherDataApiService: WeatherDataApiService
): WeatherDataRepository =
WeatherDataRepositoryImpl(
weatherDataApiService,
mapper
)
#Provides
#Singleton
fun providerApiService() = WeatherDataApiService()
#Provides
fun providerMapper() = WeatherDataMapper()
}
This is the code of the Dagger module which is in presentation(app)
#Module
class WeatherDataModule {
#Provides
#Singleton
fun provideFetchWeatherDataUseCase(weatherDataRepository: WeatherDataRepository) =
FetchWeatherDataUseCase(weatherDataRepository)
}
This is the code of the Dagger Component which is in presentation(app)
#Singleton
#Component(modules = [WeatherDataModule::class, WeatherDataRepositoryModule::class])
interface WeatherDataComponent {
fun injectWeatherDataFragment(weatherDataFragment: WeatherDataFragment)
}
Here I am creating a dagger, the code is in presentation (app)
class App : Application() {
lateinit var weatherDataComponent: WeatherDataComponent
override fun onCreate() {
super.onCreate()
weatherDataComponent = DaggerWeatherDataComponent.create()
}
}
Error text - cannot access WeatherDataApiService

Dependency injection with Koin

I have a class that uses Dagger 2 for dependency injection. Now I want to switch to Koin for dependency injection. There are modules in Koin and I want to make a module out of the class or whatever can be done.
#Module
class NetModule(private val baseUrl: String) {
#Provides
#Singleton
fun providesOkHttpClient(
httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient = OkHttpClient.Builder().addInterceptor(
httpLoggingInterceptor).build()
#Provides
#Singleton
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
val interceptor = HttpLoggingInterceptor(
HttpLoggingInterceptor.Logger { message -> Logger.d("NETWORK: $message") })
interceptor.level = HttpLoggingInterceptor.Level.NONE
return interceptor
}
#Provides
#Singleton
fun providesMoshi(): Moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
#Provides
#Singleton
fun providesRetrofit(okHttpClient: OkHttpClient, moshi: Moshi): Retrofit {
return Builder().client(okHttpClient).baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
#Provides
#Singleton
fun providesApiInterface(retrofit: Retrofit): ApiInterface = retrofit.create(
ApiInterface::class.java)
}
Koin uses a DSL for describing modules. Usually you'd declare the module itself on a top-level. Since you need to provide baseUrl, you'd have to create a factory for it.
The #Provides annotation is completely irrelevant, but #Singleton needs to be translated and does so with single. To retrieve the dependencies, just call get().
fun netModule(baseUrl: String) = module {
single {
HttpLoggingInterceptor(
HttpLoggingInterceptor.Logger { message ->
Logger.d("NETWORK: $message")
}).apply {
level = HttpLoggingInterceptor.Level.NONE
}
}
single {
OkHttpClient.Builder()
.addInterceptor(get<HttpLoggingInterceptor>())
.build()
}
single {
Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
}
single {
Retrofit.Builder()
.client(get())
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
single { get<Retrofit>().create(ApiInterface::class.java) }
}

Categories

Resources