I'm working on application which uses Jetpack Compose with Jetpack Compose Navigation. In one view (destination) I'm displaying list on entries (let's call it View A with Model A). From this view user can go to creation view (View B with Model B), where new entry can be created. After successfull creation, I want to update list in Model A, so user don't need to refresh View A after going back to see newly created entry.
Is it possible to pass ViewModel class between navigation destinations using NavHost like this or in any other way?
As per the Thinking in Compose guide:
your composables are responsible for transforming the current application state into a UI every time the observable data updates.
That application state is the source of truth. This matches the Guide to app architecture, where your state is owned by lower level components that are responsible for the actual fetching, storing and caching of data, which is then exposed to the UI layer. This layer responsible for fetching, storing, and caching data is often called the 'repository layer'.
That means that directly passing snapshots of data between destinations in your navigation graph is exactly the wrong way to approach the problem: it creates a source of truth problem (do you trust the snapshot you sent between destinations or the repository?). The answer is always the same: your repository should always be the source of truth and you should never be passing snapshots of data between destinations. In this way, every screen that uses the repository as its source of truth automatically has the most up to date information and there is never a need to 'refresh' your data.
So your architecture would include three layers:
A single repository that owns your list of entries. The most simple part of this may just be the list held in memory as a mutableStateOf<List<Entry>>() that you update when the data changes with a new list. This class would be responsible for talking to the server, caching locally, etc.
(optionally, and a best practice) a layer of ViewModels, one for Screen A and one for Screen B that expose only the sets of methods from the repository specifically needed for that screen (i.e., your ViewModel A might expose a getEntries(), while ViewModel B might expose a createEntry(Entry) method.
Screen A and B focus solely on displaying the data retrieved from their associated ViewModel. As both are talking to the same repository layer, Screen B creating an entry will update the list that Screen A will retrieve its data from.
Related
I understand that the advantage of the MVI pattern is that it is a single-state flow. So is it really necessary to have only one state model in MVI?
My app has several activities, and the subject of data obtained for each activity is completely different. For example, activity A gets the dog's information, and activity B gets the information of a Github user. In this case, if MVI-pattern should be only one state model, the mvi state model contains all the data information of activities A and B?
In most cases we're talking about single-state per ViewModel. And ViewModel is (usually, but not always) bound to a single screen. There is absolutely no incentive to store whole application state in a single object, that would get out of hand really quick
Adding to the accepted answer, Mvi is one of the presentation patterns, which addresses and try to solve the presentation problems. so by presentation we mean Single Screen whether it's an Activity, Fragment or Ui controller.
So with screen A, you'll have uni directional flow to get the Dog's information, and another one to Screen B, and so on.
Is there a difference between the "newer" Model-View-Intent architecture compared to the "older" ones like MVC and MVVM?
What issue does MVI address? What are the similarities with MVC/MVVM? What are the differences?
There are similar questions already on stackoverflow for MVC/MVV/MVP but none so far that explains MVI.
What is the difference between MVC and MVVM?
What are MVP and MVC and what is the difference?
from my experience each architecture pattern of those was invented to solve specific problem that the previous one ignored or wasn't observed yet.
MVC - Model View Controller
in UI applications the responsibilty of rendering the data to the screen, or the business logic and bind those together at first wasn't clear. so MVC came to define those responsibility to three components, each one has one purpose, and the picture describe the relation between those three components.
View - is the UI component that has all the properties like color, shape, tools to listen to the click events .. etc.
Model - is the component that define the business logic that you want the view to render and behave accordingly.
Controller - is the one who change the model, so if the view has a name for example to save, View pass it to the controller then controller manipulate the model with the right actions.
MVP - Model view presenter
the problem with MVC that there is a great coupling between the three components, if you want to change the view calls, it will require you to update controller and the Model.
and that's clear from the MVC picture, the relationship between the three components is very tied, you couldn't replace one of those components without the other.
So MVP came to provide a more clean solution to the previous problem by separating the Model and the View, keep the interactions between them via the Presenter, Presenter is the middle man that each the view and the model call.
So if you want to save a list of favorites movies, View listen to user (*) action, then call the presenter function that update the model, then model tells the Presenter if that succeed or not, and Presenter tells the View to show the right message.
MVVM - Model View ViewModel
with the rise of reactive paradigm, it was clear that we can provide more separate of concerns in UI Applications by just observing the changes and behave on it.
so for example there is a click in view that need to call an api to get latest tv shows.
this view click will be observed at the ViewModel, ViewModel interact with the model to get the data, and finally ViewModel post those data on the view using other observer ..
so in short, View observe ViewModel to get UI updates, and ViewModel observe View to call the right action with the Model. Observer pattern has proved his worthy in decoupling logic so here you go a new Pattern.
So after talking about the most popular architecture patterns, each one has tried to decouple the UI code from the business code. but the previous patterns doesn't bound updating UI with different states in the same time.
if you had an issue that related to the loading appear with an error message showed at the same time, you will understand what I'm talking about, so to maintain UI state, you have to do extra effort looking what you wrote wrong causing those kind of issues.
MVI - Model View Intent
MVI is based on an old idea called finite state machine, any system or component has predictable, set of states is a finite state machine.
in MVI any update to the UI is defined by new state, you could find this is overwhelming, but imagine that you have a screenshot for each time UI changes, that's the state. you can debug, test, reproduce the state issues now.
how to achieve this, that's the MVI in practice.
any user interaction with the UI, is defined in MVI by an Intent, Intent is what the user need from this action, it could be star a movie, refresh the screen, it even could be opening the screen, in that case the Intent is an initial intent to show the screen with all required data.
which component get those Intents to act according on them, that what you define .. you could use a Presenter or a ViewModel, it doesn't matter, MVI is more a practices than using a new middle component.
I'll continue with ViewModel, ViewModel will get those intents, decide which usecase to call (Model behaviors).
all usecases pass by summer function in the ViewModel, that decide which state that needs to be reflected to the View, it provides you with previous state too, so you have the previous and the new state to update the screen, which reduce the rendering updates, and View get only the new hints to update itself.
and finally MVI is uni directional flow, it starts with the View and ends with the View.
... View -> ViewModel/Presenter -> Model -> View -> ...
MVI is different in the way of managing the state, it's a combination of several ideas to build more stable, and testable app.
A really great breakdown is here: https://academy.realm.io/posts/mvc-vs-mvp-vs-mvvm-vs-mvi-mobilization-moskala/. At it's core, MVI is taking the ideas of MVVM (stateless UI state), separate business logic and models, and putting the reactive framework on top of it. Making things streams of events instead of discrete actions, making receiving elements consumers of transformed streams instead of presentation elements, and making state a read-only, disposable thing that is acted upon explicitly in a very structured way.
This requires that you take a functional approach to writing your application, especially the UI/View part of things. State is not modified, new state is calculated from an intent and a series of use-cases. This is fairly well explained here: https://proandroiddev.com/mvi-a-new-member-of-the-mv-band-6f7f0d23bc8a.
It is intended to address the growing complexity of modern UI applications, who have a non-trivial amount of client-side state that needs to be managed explicitly. As most experienced programmers know, the most complex failures come from state that is being modified in an unexpected way. This state manipulation can result in "invalid" states that your application cannot handle, which is effectively a crashed application. MVI addresses this by making state transitions explicit and carefully structured so that they system never comes to an invalid state, and the state is always understandable.
This is the 'like' feature on Facebook.
I would like to synchronize these recyclerviews with these two pieces.
If you click on the 'Like' button on the recyclerview in one piece, the 'Like' button on the recyclerview should change when you change to another piece.
Which method should I use?
interface?
service?
Map Should I use this?
What method do you use to synchronize the data of two fragments?
You should be using ViewModel's from architecture components.
https://developer.android.com/topic/libraries/architecture/.
Basically you create a view model in the activity so that it is stored with the activity scope
//this is the instance of the activity
ViewModelProviders.of(this)
You can then get an instance of this view model in each fragment using
ViewModelProviders.of(getActivity())
The view model can then be used like in a standard MVVM architecture.
Each fragment should register to the lifecycle aware components that the ViewModel would provide. MutableLiveData is one such component that you could use to provide the data back to whoever is interested in the data (in this case each fragment)
Be aware that LiveData while does a fantastic job can be limited as it stores data as a state in time. This is great, but android should be developed where it is driven by events)
As an example If you have a viewmodel which sends data to the view via livedata it could trigger a dialog. When the user closes that dialog and causes a configuration change (destroys and recreates the activity) the view will receive the state of the live data at the point in time it was set which will again show the dialog. Basically each time you rotate the device it could show the dialog agian even though you've dismissed it.
A hacky fix to this is to notify the viewmodel to remove the state in the livedata after the dialog is dismissed. but this creates a number of other issues including tying view state with the viewmodel
It's a lot more flexible if the Lifecycle aware component instead sends events of when data changes. Think Rxjava that is lifecycle aware. You add the data to the RXJava component and the observable provides the data to the observer when the view is in a state to consume it (> onresume and < ondestory).
Hopefully that gives you a starting point. Let me know if you need more details
TL;DR
How do I deal with Activities that actively change data (for example through an EditText)? Do I keep saving their state in the SavedInstanceState on rotation and only use the ViewModel when all of the fields are ready, or is there a way to make the ViewModel responsible for checking/holding/using the UI's data?
Question
I'm developing my application using Google's Arch. Components, and writing my latest class I've noticed I'm not really sure on what the best practice is when handling, say, data coming from an Activity form.
Example
I have a POJO made of title, description, location, type
I have an Activity with four EditText: title_et, description_et, location_et, type_et.
My ViewModel, through a Repository (irrelevant here), can send an object to the Database when the sendObject function is called.
How I'm doing it now
The activity has the mTitle, mDescription, mLocation, mType.
On rotation, the activity saves all of the EditText values in the savedInstanceState bundle, and it loads them again populating the views.
When the user wants to send the object, it clicks a button and the activity calls the function viewModel.sendObject(mTitle, mDescription, mLocation, mType) after the necessary checks.
Problems with this approach
The activity is responsible of holding/checking all the data of the EditTexts, basically making the ViewModel only responsible of interacting with the Repository.
What I'd like to accomplish
Ideally, I'd want to make the Activity only responsible of the UI, delegating everything to the ViewModel.
This way I could call sendObject() and the ViewModel would have already all of the data needed.
The LiveData situation
Right now the ViewModel has only one instance of LiveData, inside that there is a Resource (which is taken from here) and it's used to "tell" the Activity that new data has arrived or an error occurred.
This approach works fine in all Activities that just receive data from the network and display them. What do I do when I want to synchronise data coming FROM the Activity? Do I use one LiveData for each field and use that to display potential errors?
I've read most of the samples but all of the Activities there are passive.
Conclusion
Thanks to anyone who takes the time to help.
You can either separate the logic into a model string class with another class containing all your String values for the edit text fields are just assign the String values at the top of your class.
You can have a LiveData of your model in the ViewModel and alter it from the View (Activity/UI). The downside is that to update the LiveData, you need to copy whole Model, edit it and post it back to live data.
The second way is to dissect Model's components in the ViewModel into individual parameter LiveDatas. Later when form is submitted you can reconstruct the Model.
What you can do for native fields is use data binding. For other you need manually update LiveData from the View with listeners etc.
On MVVM pattern, the ViewModel contains business logic and notifies the View when it needs to be updated. It is also notified by the view about user events.
As I understood it, each Model should have an associated ViewModel. So, if we have the following models:
User
Account
We would have the following ViewModels:
UserViewModel
AccountViewModel
However, all examples I find about data binding with MVVM, use a single ViewModel for a layout. And recently, Google has introduced the ViewModel class within Architecture Components. This leads me to believe an Activity would have a single ViewModel that would connect to all related Models:
User / Account --> ActivityViewModel
This gets even more complicated if we think of a RecyclerView. Each adapter item could be a ViewModel itself, so an Activity with a RecyclerView would have multiple ViewModels within the list and plus a master one for the remaining view contents (assuming they require information from a ViewModel). For instance:
In this example, we have a list of Account ViewModels and one UserViewModel. How would this be embedded into a single ActivityViewModel?
You should have one ViewModel per View (Activity, Fragment or Custom View), with multiple LiveData, one for each logical unit. In the image one logical unit would be the user data, another logical unit would be the settings data, so you would have two LiveData exposed in the ViewModel.
These concepts can also be visible in the recommended app architecture google presented in the last Google I/O, where an Activity/Fragment has 1 ViewModel with multiple LiveData:
Google suggests that you use 1 ViewModel per View (i.e., Activity or Fragment) (see https://youtu.be/Ts-uxYiBEQ8?t=8m40s), and then inside each ViewModel you can have more than 1 type of Model. However, the MVVM principle is to have only 1 Model type per ViewModel, so Google's presentation contradicts that :/. I guess you'll have to decide what approach works better for your app.
About the list example you mentioned, that is not how you'd do it, for lists you'd use the paging library. You can see details on how to use this at the end of the video I linked above.
One view model is standard. However, even google suggests that you may have more than one view model. That comes quite convenient when you mitgrate a phone app towards a tablet app. When you combind more than one phone views on a single tab view, then it is convenient re-using the models.
If you have the SOLID principles in mind when coding, then the S (single responsibitly of a class) may cause you to use more than one view model.
However, one per view is pretty much standard and you shold have reasons, as metioned above, to use more than one.
What is the relationship between Users and Accounts? If these are two separate, unrelated models, then they should each have their own views and view models. Remember the single responsibility principle: each module should be responsible for only a single part of your logic. That way, any changes to your domain logic or models will only affect that part, and that part only.
In general it is okay to have 1 View with multiple ViewModels.
View - answers the "how", how should a UI rendered. But it should never answers the "when".
ViewModel - answers the "when", it's the presentation logic and tells when should the view be rendered. ViewModel should never have reference to any View. Instead View observes and listen on ViewModels.
Model - answers the "where", it's the business logic which handles data (local/remote) request. Where to read data and where to write.
As long as ViewModel do not have any reference of View, it should be okay since we can have a situation with Fragment that has its own ViewModel and another ViewModel which comes from its host Activity. We also have a so called shared ViewModel which uses by activityViewModels() that can be use by the Fragments inside the Activity.
You will have a Fragment that has 1 ViewModel own by the fragment itself and 1 ViewModel own by the host Activity.