Search code examples
androiddependency-injectiongoogle-cloud-firestoredagger-2dagger

How to inject an object using a value obtained in an activity?


I have an activity where I get the uid of a user from Firebase authentication:

public class MainActivity extends DaggerAppCompatActivity {
     @Inject UserViewModel userViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
        userViewModel.setUid(uid);
    }
}

I'm using this uid in my view model class:

public class UserViewModel extends ViewModel {
    private CollectionReference usersRef;

    @Inject
    UsersViewModel(CollectionReference usersRef) {
        this.usersRef = usersRef;
    }

    void setUid(String uid) {
        DocumentReference uidRef = usersRef.document(uid);
        //Use uidRef
    }
}

And this my app module class:

class AppModule {
    @Singleton
    @Provides
    static FirebaseFirestore provideFirebaseFirestoreInstance() {
        return FirebaseFirestore.getInstance();
    }

    @Singleton
    @Provides
    static CollectionReference provideUsersCollectionRef(FirebaseFirestore db) {
        return db.collection("users");
    }

    @Singleton
    @Provides
    static DocumentReference provideUidDocumentRef(CollectionReference usersRef) {
        return usersRef.document(uid); //How to add the uid here
    }
}

The question is, how can I add the uid in the app module class, so I can inject the DocumentReference directly in my view model class like this:

public class UserViewModel extends ViewModel {
    private DocumentReference uidRef;

    @Inject
    UserViewModel(DocumentReference uidRef) {
        this.uidRef= uidRef;
        //Use uidRef
    }
}

Solution

  • You have the correct mindset in trying to inject dependencies but unfortunately, not all dependencies can be provided via constructor injection, some of them need some information that is only known at runtime.

    Look at your code, UserViewModel is injected in MainActivity, some of its dependencies were provided via constructor injection but you have to provide its uid dependency via a setter injection in the line userViewModel.setUid(uid); once the uid is retrieved at runtime.

    Your code seem fine in retrieving the DocumentReference from the list once the uid is known. Maybe change your code in MainActivity to retrieve the DocumentReference and provide it via setter to UserViewModel?

        String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
        userViewModel.setDocumentReference(db.collection("users").document(uid));
    

    If you really want to inject the DocumentReference directly in your view model class like you described, you can add the uid as a constructor parameter to your AppModule.

    But assuming that your AppModule is used by your AppComponent which is created in your Application class, you won't have the uid in the Application class to create the AppModule.

    So you will have to create another component for your UserViewModel class, something like UserComponent. And create a UserModule which receives the uid as parameter in the constructor.

    Then in your MainActivity, once you retrieve the uid, you create the UserComponent:

        UserComponent userComponent = DaggerUserComponent.builder().userModule(new UserModule(uid)).build();
        userViewModel.setUserComponent(userComponent);
    

    and this is how your UserViewModel will look like:

    public class UserViewModel extends ViewModel {
        @Inject    
        private DocumentReference uidRef;
    
    
        public void setUserComponent(UserComponent userComponent) {
            userComponent.inject(this);
        }
    }