What is the better MVVM approach to define a ViewModel class? - android

I've seen many tutorials for MVVM. Most of them say that you need to define your ViewModel class like this:
class MainViewModel: ViewModel() {
...
}
But recently I stumbled upon Dagger tutorial project from Google. There is a different ViewModel class definition:
class MainViewModel(private val userDataRepository: UserDataRepository) {
...
}
So I wonder, what is the difference between these two approaches?

That's not a relevant comparison. That CodeLab uses a non-ViewModel ViewModel class to simplify their explanation of how DI works. Notice it doesn't subclass ViewModel. Also, the project starts without the dependency injection and has you add it in later, so the starting project isn't intended to be a good example of how to design something.
Either way, if you have a repository, you need some way to get a reference to the repository in your ViewModel. If it is through the constructor, you would have to get a reference to the repository in the associated ViewModelFactory that you build for this class. If you use Dagger, you'll probably let Dagger generate this factory for you and inject the reference.
If your ViewModel doesn't use a repository, then you won't have any reason to have one in your constructor, with or without dependency injection. Many basic MVVM tutorials are going to start with the most basic possible example, a ViewModel with no arguments needed. That doesn't imply that a ViewModel should never have dependencies.

Related

Is it good way to inject common class with dependency injection?

I inject viewModels,repositories,fragments,Utils and ... but Is it good way to inject common class with dependency injection Koin or Dagger-hilt?
Suppose that we want to use StringBuilder() class in fragment
First approach :
We can inject it
val otherModule= module {
single { StringBuilder() }
}
And use it in fragment like this :
class Fragment : BaseFragment(){
private val mPassword : StringBuilder by inject()
}
Second approach :
We can create new instance without injection
class Fragment : BaseFragment(){
private var mPassword = StringBuilder()
}
My question is the first approach is common way for us ?
I'd say that it depends. The main goal / concept behind Di is to fulfil the firth principle of S.O.L.I.D, or as freecodecamp.org says:
It is the fifth principle of S.O.L.I.D — the five basic principles of
object-oriented programming and design by Uncle Bob — which states
that a class should depend on abstraction and not upon concretions (in
simple terms, hard-coded).
According to the principles, a class should concentrate on fulfilling
its responsibilities and not on creating objects that it requires to
fulfill those responsibilities. And that’s where dependency injection
comes into play: it provides the class with the required objects.
Another good answer is provided here, as it is already discussed when not to use dependency injection.
And now my opinion: If possible, try to inject those dependencies that you have to use quite often and if possible, inject them via constructor injection. With this, you can easily see, which dependencies are used by your class. Try not to inject classes that are used once or common language classes.
No, you wouldn't inject common language classes at all. There's no benefit to it. We inject classes when they need additional dependencies that we don't want to be creating ourselves, so we let the DI framework create them.

Inject into arbitrary Logic class that is not instanciated by Hilt

I'm currently migrating an app from anko-sqlite to room as anko has been discontinued a long time ago. In the process of doing so I introduced a repository layer and thought I would try to introduce dependency injection to get references to the repository instances in all my classes.
Prior to this I used a singleton instance that I just shoved into my application classes companion object and accessed that from everywhere.
I managed to inject my repositories into Fragments, Workmanager and Viewmodels. But I do now struggle a bit to understand how they forsaw we should handle this with arbitrary logic classes.
For instance my workmanager worker calls a class that instantiates a list of "jobs" and those need access to the repository. The worker itself actually bearly even does need access to the repositories as all the work is abstracted away from it.
How can I make something like this work
class Job(val someExtraArgINeed: Int) {
#Inject
lateinit var someRepository: SomeRepository // <= this should be injected when the job is instanciated with the constructor that already exists
}
For Activities and so forth we have special annotations #AndroidEntryPoint that makes this work. However, the documentation makes it unclear as to how we should get our instances from any other class that isn't an Android class. I understand that constructor injection is the recommended thing to use. But I do not think it would work here for me without an even more massive refactor required than this already would be.
If I understand your question correctly you can use hilt entry points like this
class CustomClass(applicationContext: Context) {
#EntryPoint
#InstallIn([/*hilt component that provide SomeRepository*/ApplicationComponent]::class)
interface SomeRepositoryEntryPoint {
fun provideSomeRepository(): SomeRepository
}
private val someRepository by lazy {
EntryPointAccessors.fromApplication(applicationContext, SomeRepositoryEntryPoint::class.java).provideSomeRepository()
}
fun doSomething() {
someRepository.doSomething()
}
}

How to mock the view model with Hilt for unit testing fragments?

I've got an android app setup for dependency injection using Hilt, and would like to unit test my fragments.
I'm currently creating my view model using:
private val viewModel: ExampleViewModel by viewModels()
And I am creating the fragment for testing using the code from here
I need to replace this ExampleViewModel with a mock, how would I go about doing this?
I will paste here the "danysantiago" response in a issue (https://github.com/google/dagger/issues/1972) related to your question:
Hilt ViewModel extension works by declaring modules that bind assisted
factories to a map and not by binding concrete ViewModels. Therefore,
what you want to do is bind the assisted factory of the concrete
ViewModel using the key of the abstract ViewModel so that when
HiltViewModelFactory looks up the factory based on class key it uses
the assisted factory for the concrete ViewModel. This is suuuper
obscure and hence why I mean not 'easily' available.
However, if you can expand on the test case your are trying to write
that could help us provide some guidance, I'm not sure if you are
trying to mock/fake the ViewModel itself for tests, but Hilt testing
APIs should allow you to replace dependencies in the ViewModel so you
can write a test with the Fragment and the ViewModel.

ViewModelFactory need

I'm doing some codelabs of kotlin fundamentals and I don't really get in android with the ViewModel why sometimes there seems to be a need of creating it through a ViewModelFactory. Here you can see the codelab which talks about this.
They just say to perform the initialization using the factory method pattern but I don't understand the why. Why do we need to use factory pattern? Is it because we need to pass some parameter to the ViewModel? Or is it for some other reason? Is every time we need to create a ViewModelFactory just to pass parameters to the ViewModel?
I've been looking for the answer, trying to confirm whether is just to pass extra parameters or is because of any other reason but still I'm not sure and I haven't found the answer.
There are a few things that need to consider before using ViewModel and ViewModelFactory
ViewModel is LifecycleAware Components.
ViewModel survive configuration changes.
ViewModelProvider' can only instantiateViewModel` with no-args contructor.
Why do we need to use factory pattern?
To instantiate ViewModel with arguments need to use ViewModelFactory. ViewModelProviders Utility can not create an instance of a ViewModel with argument constructor because it does not know how and what objects to pass in the constructor.
Also, you should follow the Dependency Injection principle. A class should not create dependencies it needs. It should be provided rather than creating.
For Example -
public class LogInViewModel extends ViewModel {
private final LogInRepo repo;
public LogInViewModel (LogInRepo repo) {
/* this.repo = new LogInRepo(); Not recommended, It violates DI principle*/
this.repo = repo;
}
}

Dagger 2 - Strategies for reducing number of classes which require annotations

So I am currently in the process of learning dagger 2, and from the tutorials that I've read so far, for a dependency to be injected, the #Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work, or if I don't want other parts of the code to know that dagger exists.
The application structure I have at the moment is:
App Module - where I'd like to put my DI code in (e.g. dagger modules, etc).
Presentation Module - Views/ViewModels etc.
Domain Module - Use Cases etc.
Data Module - Repositories etc.
With pretty much this style of classes contained in my application:
class ExampleViewModelImpl(useCase: ExampleUseCase): ViewModel() in Presentation (gets initialised from an Activity or similar).
class ExampleUseCaseImpl(repository: ExampleRepository): ExampleUseCase in Domain
class ExampleRepositoryImpl(dao: ExampleDao): ExampleRepository in Data
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible? Code examples of how this is achieved would be great.
I am unsure of some terminologies, and wasn't able to find a solution online. If there are good resources which explains what I'm asking, that would also be great.
if I don't want other parts of the code to know that dagger exists.
#Inject is a standard (JSR 330) which Dagger implements. Adding those annotations doesn't have anything to do with Dagger and can be used the same way with other DI frameworks. If it's your code you should just add those #Inject annotations where appropriate. Think of them as documentation: What constructor/field/method must be injected to create & use this object?
The only place where your classes will know that Dagger exists is at the same place where you'd be creating the objects otherwise, too.
Going down that path, of course you could use Dagger without any #Inject annotations, but you'd be writing a lot of unnecessary boilerplate and missing out on the most powerful feature of Dagger at the same time (code generation).
#Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work
That's what #BindsInstance with the #Component.Builder is for (add an object to the component) and what #Provides annotated methods are for (create and initialize an object from a module)
If you really want to write code without #Inject, then you'd do exactly this for all of your objects. This means a lot of modules, and even more #Provides annotated methods. It will work, but I don't see the point in writing all those methods if a single #Inject on the constructor has the same effect.
In my opinion the best thing about Dagger is that I can add / remove / change constructor parameters and don't have to touch any other parts of my code since Dagger will generate new code with the new arguments. In your case you'd have to also change the parameters to the #Provides method as well as the constructor invocation.
Next let's look at how to remove #Inject from fields. Basically you don't want to do field injection, so instead of writing an injection method in the component, you'd write provision methods.
#Component
class MyComponent {
fun inject(activity: MyActivity)
}
class MyActivity {
#Inject lateinit var myDep: Dependency
fun onCreate() {
component.inject(this)
}
}
Removing the #Inject we need to use the provision methods instead.
#Component
class MyComponent {
fun provisionMyDependency() : Dependency
}
class MyActivity {
lateinit var myDep: Dependency
fun onCreate() {
myDep = component.provisionMyDependency()
}
}
It will work and everything, but again, you will miss out on the single best feature of Dagger: Code generation. The example above looks alright because I only added a single dependency, but think about what happens to those 2 different implementations when you add / remove / change dependencies, how well it will scale. If you prefer to do things manually any refactoring will become arduous.
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible?
Your question (especially the title) is in direct conflict with your goal. If you don't want to use those annotations, then you can't use Dagger code generation & injection but have to resort to do it manually as highlighted above.
with as much automated dependency injection as possible
To best utilize Dagger you add #Inject on the constructor and/or fields of every class that should end up on your dependency graph and let Dagger do its thing.

Categories

Resources