sczerwinski / android-hilt

Extensions for Dagger Hilt
https://czerwinski.it/projects/android-hilt/
Apache License 2.0
47 stars 1 forks source link

hilt-processor-ksp error: "'public' function exposes its 'internal' parameter type" if implementation is internal #293

Open n0vah opened 1 year ago

n0vah commented 1 year ago

I have multi module project and inside module I have public interface

public interface UserRepository {
  ...
}

and internal implementation of this interface

@Bound
internal class UserRepositoryImpl @Inject constructor(): UserRepository {
  ...
}

With kapt processor It works fine because it's Java and it doesn't know about "internal" modifier:

@Module
@InstallIn(SingletonComponent.class)
public interface UserRepository_SingletonComponent_BindingsModule {
  @Binds
  @Singleton
  @NonNull
  UserRepository bindUserRepositoryImpl(@NonNull UserRepositoryImpl implementation);
}

But with ksp processor I'm receiving an error 'public' function exposes its 'internal' parameter type UserRepositoryImpl:

@Module
@InstallIn(SingletonComponent::class)
public interface UserRepository_SingletonComponent_BindingsModule {
  @Binds
  public fun bindUserRepository(implementation: UserRepositoryImpl): UserRepository
}

Would be good if class that marked with annotation @Bound/@BoundTo has "internal" visibility modifier, than generated "*_BindingsModule" class would also have internal visibility modifier.

sczerwinski commented 1 year ago

I'm concerned how Dagger and Hilt will handle internal module classes when they finally migrate to KSP.

Also, I'm a bit reluctant to implement an internal module in the first place. If the module is the single source of UserRepository in the entire project (SingletonComponent), then making it internal seems a bad smell to me.

Even if we agree that this change should be made, internal implementations should be separated from public implementations. Putting both in the same module could potentially break the latter. *_Internal suffix is probably a good idea in this case.

I'll look into this, but in the meantime, here's a possible workaround (instead of @Bound/@BoundTo):

public interface UserRepository {

    // ...

    companion object {

        @FactoryMethod
        fun getInstance(): UserRepository = UserRepositoryImpl()
    }
}

This way, internal implementation is not exposed by the public factory method, while the single source of UserRepository is public.

EDIT: This will only work with KSP, as Kapt processor does NOT support factory methods in companion objects.