https://kotlinlang.org logo
#dagger
Title
# dagger
h

Hovhannes

08/18/2021, 10:29 AM
Hello, I get this error. What is the reason? " error: @Binds methods' parameter type must be assignable to the return type public abstract com.example.data.network.AuthApi " in AppModule
Copy code
@Module
@InstallIn(SingletonComponent::class)
abstract class AppModule {


    companion object {
        @Singleton
        @Provides
        fun provideRemoteDataSource(): RemoteDataSource {
            return RemoteDataSource()
        }



        @Singleton
        @Provides
        fun provideUserApi(
            remoteDataSource: RemoteDataSource,
      
        ): UserApi {
            return remoteDataSource.buildTokenApi()
        }

        @Provides
        fun provideAuthRepository(authApi: AuthApi): AuthRepository {
            return AuthRepository(authApi)
        }


        @Provides
        fun provideUserRepository(userApi: UserApi): UserRepository {
            return UserRepository(userApi)
        }
    }



        @Binds
        @Singleton
        abstract fun bindsRemoteDataSource(authRepository: AuthRepository): AuthApi
}
Copy code
class AuthRepository @Inject constructor(private val api: AuthApi) : BaseRepository(api) {....}
a

Arun

08/18/2021, 10:52 AM
params of the
@binds
should be a subclass of the method return type is
AuthRepository
subclass of
AuthApi
?
Copy code
@Binds
        @Singleton
        abstract fun bindsRemoteDataSource(authRepository: AuthRepository): AuthApi
h

Hovhannes

08/18/2021, 11:14 AM
@Arun thanks for your reply. No 
AuthRepository
 is not subclass of 
AuthApi
. I can show Authapi.
Copy code
interface AuthApi : BaseApi {

    @FormUrlEncoded
    @PUT("{PartnerId}/LoginClient")
    suspend fun login(
        @Field("name") name:String,
        @Path("partnerId") partnerId:String,
        @Field("Password") password: String
    ): LoginResponse
}
a

Arun

08/18/2021, 11:23 AM
That is the issue. By writing
bindsRemoteDataSource(authRepository: AuthRepository): AuthApi
you are asking dagger to bind
AuthApi
interface to
authRepository
which is an invalid connection. Did you mean to bind
AuthApi
instead? I assume this is for Retrofit, instead what you can do is to write a @Provides method similar to ones you have already written and return the instance created with Retrofit builder for example like this
Copy code
@Provides fun authApi() {
  Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("<https://api.github.com/>")
    .build();

 return retrofit.create(AuthApi.class);
}
h

Hovhannes

08/18/2021, 11:38 AM
@Arun I want to say that if I don't use the @Binds method, I get this error "AuthApi cannot be provided without an @Provides-annotated method". So why I use the @Binds method
a

Arun

08/18/2021, 11:41 AM
That is correct, @Binds can be used only when the instance to be bound is already available i.e dagger knows how to create an instance of class you want to inject. In this, Dagger can't know how to instantiate the class and Retrofit is the one that can correct implementation of AuthApi. Hence instead of using @Binds, you need to use @Provides since @Provides allows you manually create the instance instead of dagger attempting to create it.
AuthApi cannot be provided without an @Provides-annotated method
Dagger is basically saying it can't create instance of AuthApi and neither there is a @Provides method to help create one.
h

Hovhannes

08/18/2021, 11:46 AM
@Arun what do you offer for fixing the issue? I can't use the below code, because url is not constant, it changes every time.
Copy code
@Provides fun authApi() {
  Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("<https://api.github.com/>")
    .build();

 return retrofit.create(AuthApi.class);
}
a

Arun

08/18/2021, 11:56 AM
In that case you would need to make the url configurable by
Copy code
@Provides fun authApi(@Named("baseUrl") baseUrl : String) {
  Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(baseUrl)
    .build();

 return retrofit.create(AuthApi.class);
}
Then create another binding for baseUrl like done here https://github.com/chrisbanes/tivi/blob/main/app/src/main/java/app/tivi/inject/AppModule.kt#L76 Will the API url change at runtime? If so you can return a retrofit builder instead and build api instance at runtime like
Copy code
@Provides fun retrofitBuilder() : Retrofit.Builder() {
 return  Retrofit.Builder().apply {
   // any other config
 }
}

///
class AuthRepository @Inject constructor(private val apiBuilder: Retrofit.Builder) : BaseRepository {
   private val authApi by lazy {
     
     apiBuilder.baseUrl("<url here>").build()
   }
}
h

Hovhannes

08/18/2021, 12:47 PM
@Arun I want to say that buildApi() was in RemoteDataSource. But now I want to move buildApi() to a fragment, because I need to pass a data as buildApi()'s parameter. I do it, because I haven't fix this issue till now https://kotlinlang.slack.com/archives/C0B8M7BUY/p1629201848365200
387 Views