Let's say I'm starting a new application and I hav...
# dagger
c
Let's say I'm starting a new application and I have a
CrashReporterService
interface, and a real impl called
FirebaseCrashReporterService
. Both the interface and impl are on the Dependency graph, and so... nothing prevents someone from Injecting FirebaseCrashReporterService. Is there anything I can do to hide my real impls so that people only use the interface?
n
I'm not aware of a way to hide it completely but I'll always point this out in the documentation for the class. Something like "this is the default implementation of
CrashReporterService
if you're using Dagger favor just injecting the interface"
👍 1
j
Use a qualifier annotation on the impl that is not public
👍🏼 1
👍 5
d
If you’re using modules, you can consider this approach too: https://www.zacsweers.dev/dagger-party-tricks-private-dependencies/
u
or just use module with provider method which has the interface return type, but return the real impl why this was discouraged is beyond me
c
jw, you're basically saying to use what Zac has in that article? That makes sense to me, but now I lose the ability to just have my impl have @Inject? Now I need to create a provider for it even though I own the class? (I tried to add it directly onto the class but no luck)
👌 1
@ursus that's what I'm doing, but the real impl will be available for anyone else on my team to inject accidentally. Short of creating a lint issue, I was curious if something could be done to prevent this at compile time. The qualifier works, but now I just need to figure out if I can use a qualifier on classes I own.
u
No it wont, why
@Provides fun foo(): Foo = FooImpl()
c
Oh gotcha. Misunderstood your recommendation. I guess you lose some of the benefits of dagger wiring up your dependency graph though if FooImpl requires any dependencies itself, but that does seem like an easy way out in this situation that I'm in. 😅
u
Well, I got used to writing modules for everyting and never using @Inject ctors, now as I move to multiplatform, im happy Modules also allow you to create private dependencies in the providers, you dont need to worry about them being public & slowing down dagger needlessly
j
In doing that, of course, you're making the runtime slower because of the added indirection. It's probably not a big deal at a small scale, but this stuff adds up at a large scale.
c
Cool. thanks for the additional info @jw I'll use that approach as well and I'll stop using @Inject
u
@jw you meam the qualifiers make the runtime slower or what I said?
j
Qualifiers themselves have no runtime cost. But modules do, especially ones which require instances. If you use modules you want to try to always use ones which do not require an instance by putting the provider methods on the companion.
u
Yea I use object Module + @JvmStatic so R8 can optimize the instance away. But using this, is still more costly than @Inject ctor?
c
This is basically what I ended up with
Copy code
interface CrashReporterService {
  fun logFatal(e: Exception)
}

class FirebaseCrashReporterService : CrashReporterService {
  override fun logFatal(e: Exception) {
    // do something
  }
}

@Module
@InstallIn(SingletonComponent::class)
abstract class AppInterfaceModule {
  @Singleton
  @Binds
  abstract fun bindsCrashReporterService(
      @Named("placeholder_qualifier") service: FirebaseCrashReporterService
  ): CrashReporterService
}

@Module
@InstallIn(SingletonComponent::class)
class AppImplModule {
  @Singleton
  @Provides
  @Named("placeholder_qualifier")
  fun bindsCrashReporterService(): FirebaseCrashReporterService {
    return FirebaseCrashReporterService()
  }
}
u
that feels like the lose lose to me. why are you even putting in the FirebaseCrashReposterService to the graph, I'd create a firebase-crash-reposter module, which depends on the crash-reported module, extend the interface, create dagger module which puts in the firebase impl for the interface type
c
very much getting "i have no idea what im doing" vibes at this point. I thought my code looks pretty good as it hides the FirebaseCrashReposterService
u
if you're not going to use @Inject constructor do just this
Copy code
@Module
object FirebaseCrashReporterModule {
    
    @JvmStatic
    @Provides
    fun firebaseCrashReporter(dependencies..) : CrashReporter {
        return FirebaseCrashReporter(...)
    }
}
that's it
c
gotcha. I just thought that's what JW was saying that we should be weary about doing, but I guess that module is an
object
so it should be fine?
u
From what I know, only difference Module having runtime instance, so if you use object+static methods then R8 should factor it away (and dagger sees object as a static class anyways, so it doesnt create module instance)
👍 1
👍🏼 1