I am writing an Android app which makes use of Firebase Auth and DI.
In this app I regularly need to use the user's UID to perform some database operations, and in search of a way to stop passing the UID through MVVM layers, I've decided to give dependency injection a try.
My AppModule looks like this:
AppModule.kt
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Singleton
@Provides
fun provideServer(@ApplicationContext c: Context) = Server(c, Volley.newRequestQueue(c), provideUid())
@Singleton
@Provides
fun provideFirebase() = FirebaseSource(FirebaseAuth.getInstance())
@Singleton
@Provides
fun provideFirebaseAuth() = Firebase.auth
@Singleton
@Provides
fun provideFirebaseUser(): Boolean {
return provideFirebaseAuth().currentUser != null
}
@Singleton
@Provides
// FIXME
fun provideUid(): String {
return provideFirebaseAuth().currentUser?.uid ?: ""
}
}
Function provideUid() returns the Firebase current user's UID, and injects is as a dependency in a separate Server class:
Server.kt (part of)
class Server @Inject constructor(
private val c: Context,
private val volleyRequestQueue: RequestQueue,
private val uid: String
): IServer {
When the app starts for the first time, Dagger Hilt uses this constructor and provides a null UID because there is no logged in user at the moment. This behaviour persists for the rest of the app's lifecycle, and the Server class never has a non-null UID because it was already constructed with an empty UID as a property.
Restarting the app will construct the Server class with a non-empty UID because Firebase Auth keeps some registry of the user's auth state.
My question, then, is whether Dagger Hilt can realise the UID has changed and update its value or not.
Things I've tried:
- Deleting the
@Singletonannotation fromprovideUid() - Changing the
Server'suidparameter fromvaltovar.
CodePudding user response:
You can try:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun provideServer(@ApplicationContext c: Context, queue: RequestQueue, uid: String) = Server(c, queue, uid)
@Provides
@Singleton
fun provideResponseQueue(@ApplicationContext c: Context) = Volley.newRequestQueue(c)
@Singleton
@Provides
fun provideFirebase() = FirebaseSource(FirebaseAuth.getInstance())
@Singleton
@Provides
fun provideFirebaseAuth() = Firebase.auth
@Singleton
@Provides
fun provideFirebaseUser(): Boolean {
return provideFirebaseAuth().currentUser != null
}
@Provides
// FIXME
fun provideUid(): String {
return provideFirebaseAuth().currentUser?.uid ?: ""
}
}
If an instance of the Server object is created once in the application, this solution should not work.
CodePudding user response:
First, you very rarely want one @Provides method to call another. It is usually better to let Dagger wire up dependencies between those methods for you.
Dagger will never recreate or update an object that it has already created. If you want a reference which will always return the current uid, you can inject either FirebaseAuth directly or a Provider<String>.
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
// Consider making a @Singleton @Provides method for RequestQueue.
// That would let you use the @Inject annotation on Server's constructor.
@Singleton
@Provides
fun provideServer(
@ApplicationContext c: Context,
uidProvider: Provider<String>
) = Server(c, Volley.newRequestQueue(c), uidProvider)
@Singleton
@Provides
fun provideFirebase() = FirebaseSource(FirebaseAuth.getInstance())
@Singleton
@Provides
fun provideFirebaseAuth() = Firebase.auth
@Provides
fun provideFirebaseUser(auth: FirebaseAuth): Boolean {
return auth.currentUser != null
}
// This should not be a singleton.
// Consider using @Named or a custom qualifier for common types like String.
@Provides
fun provideUid(auth: FirebaseAuth): String {
return auth.currentUser?.uid ?: ""
}
}
