Where should I create a model instance for a view in SwiftUI? - mvvm

I am new to MVVM. As far as I understand, I must avoid using model code inside a view struct.
In my case, I have 2 views, MainView and ChildView. MainView doesn't have a ViewModel. But ChildView has a ViewModel(e.g. ChildViewModel). Since the ChildViewModel is used only in ChildView, so I didn't register the model instance on EnvironmentObject or I didn't pass the instance to MainView, because MainView doesn't use the model at all.
I think, the best way is that ChildView creates its own instance of the model by itself like below. But I am not sure whether it is ok or not. Does this break the rules of MVVM?
struct ChildView: View {
#ObservedObject var childViewModel = ChildViewModel()
var body: some View { ... }
}
Thanks in advance.

I diagree with accepted answer. I'll provide my arguments.
MVVM is NOT about having an object called view model. Most MVVM devs are just doing it for the sake for having an object called view model.
because MVVM about "responsibility separation"
What design pattern is NOT about responsibility separation?
Why do you think SwiftUI SDK design does not provide responsibility separation?
I must avoid using model code inside a view struct
First view struct is not a view, it's a model that conforms to view.
E.g.; struct Model: View
This is designed so that you can do model -> view binding (and optionally view -> model binding) easily.
There are annotations like #State, #StateObject built specifically to support it.
E.g.; re-renders when #State changes, compiler checks to prevent access #State object from outside struct Model: View.
In other words, struct Model: View is the place to write model codes! By design!
Note that it is struct Model, not class Model. MVVM does not take value type into consideration. So instead of taking advantage of the immutability of value type, MVVM has you believe that a separate model in a reference type is a good idea? It is not.
I'm going to say this again. A model in a REFERENCE type for "responsibility separation".
There are legacy reasons that you have to create a separate View Model. It depends on how the binding is designed. It's entirely feasible that you need a separate view model for binding to work in some common language.
But Swift is not some common language.
I don't know why MVVM devs think SwiftUI SDK creators are idiots and don't know anything about MVVM. The truth is that, SwiftUI has a binding design so efficient, you don't need a separate view model to describe binding.
Hell, even I can see that you now have full advantage of binding and automatic view update from value type model, the most iconic MVVM features, without the overheads of manually creating a separate view model. And I'm not a MVVM dev.
SwiftUI makes view model by default. I've seen attempts of building a view model on top of it times and times again. They all failed miserably, because view model is dead-weight now.
You don't need it, and in fact you lose SDK support when you use it.
Or think of it this way. SwiftUI removes UIViewController.
Do you see MVC developers come out and insist you create a separate Controller object?
The same argument applies. E.g.;
because MVC is about "responsibility separation", and provided pattern
is exactly separates View & Control. Moreover it follows "dependency
injection" rule as well
Don't let these abstract words confuse you. There are basic principles that apply to everything.
To your question, it's an inefficient form of MVVM. ChildView should be your model.
And that is more MVVM than what these MVVM devs told you.
E.g.;
struct ChildView: View {
#State var value: Value
#StateObject var resource: JSON
var body: some View { ... }
}

Does this break the rules of MVVM?
No, it does not, because MVVM about "responsibility separation", and provided pattern is exactly separates View & ViewModel. Moreover it follows "dependency injection" rule as well, because you can use and
ChildView() // with default model
and
ChildView(childViewModel: ChildViewModel(...)) // some specific model

This does not violates the MVVM pattern. If your main view needs to pass some parameters to child view it should do so by using init parameters. However, one thing to note is, everytime you navigate to child view from your main view, a new instance of ChilViewModel would be created. In use cases, where this is not acceptable, a view model is created in the parent view and passed on to the child view to maintain the same instance everytime a user navigates to child view. Both do not violate the MVVM pattern. Hope that answers your question.

Related

How to Keep state of my View Model persistent across a user session in SwiftUI (MVVM)?

I am trying to implement an IOS App following the MVVM architecture. Everything works well when I don't need my state to be persistent as I move through views and navigate back.
However, when a user navigates two or three steps back to View, I want my view to appear the same way than when it was left. For this, my reasoning is that I need to make my ViewModels persistent and not have them disappear when the view gets destroyed. So far, they disappear because I create them when I instantiate the View.
My question is:
1- Is this the right way to think about it? (i.e keep my ViewModels persistent)
2- What is the standard way to achieve persistence of the viewModels within the MVVM framework?
For context (if useful): Currently my view hierarchy is implemented with Navigation Links
Thanks!
You can instantiate your viewmodel in the App struct (or somewhere else, like the first view a user encounters) then pass on that same instance of the viewmodel to deeper views. A common way to do this is by using #StateObject to instantiate and pass it on as an environment object.
You pass the environment object once and then all views lower in the hierarchy gets access to it.
So #StateObject var viewModel = ViewModel() in your App struct. Pass this viewModel to the first view in the hierarchy inside the WindowGroup like so:
ContentView()
.environmentObject(viewModel)
Now any views under ContentView in the hierarchy can access the viewModel instance through #EnvironmentObject var viewModel: ViewModel.

Navigating to a Page versus a View

I am playing around with Xamarin.Forms (mvvm) with Prism and have noticed that some tutorials show navigating to another Page while others show navigating to a View.
At a high level, I understand the literal difference... however I do not understand when I should use one over the other? I have an inclination that some of the reasoning is around dependencies, for example:
A Page has the instance of User > navigate to a view = User is still present when the back operation is used... meanwhile if you want this same behavior in navigating to and from a page, you'll need to pass the instance around via parameters... Is this correct/the reasoning behind navigating to and from views instead of pages?
In X.Forms View's are only visual objects, they do not support any navigation or any kind of infrastructure. They can only be showed when you navigate to a Page that hosts them, it doest not make sense the phrasing navigate to a View. So you should only use Page's.. In your example project they also only navigate to Page's. In the one you say they are navigating to "Views" it is only in the name, because ViewA is a ContentPage
Technically, in pure MVVM, the ViewModel should know completely nothing about the View and vice versa. When you use Page First Navigation approach you violate the first sentence. Here is an example:
class MyViewModel
{
}
class MyView
{
public MyView()
{
InitializeComponent();
// Alternatively you can do the same thing in XAML
this.BindingContext = new MyViewModel();
}
}
As you see the View is aware of the ViewModel.
When you use a ViewModel First Navigation approach, the decision of which
View should have witch ViewModel is delegated to a dedicated class. This class then is used in a custom NavigationService to match a ViewModel to a View. So it will be possible to navigate from a ViewModel to a ViewModel. This way both ViewModel and the View know nothing about each other. The disadvantage of this approach is complexity.
This is a very short answer, however, I hope you will get the key point. There are many examples of both approaches:
ViewModel First Navigation
Page First Navigation
P.S.: Prism has very nice mechanism that handle navigation. What I wrote above and the examples that I provided are just for low level understanding of this approach. If you want to use Prism you definitely have to be familiar with it.

MVVM ViewModel Naming

I have the views named as actions such as "SelectMethod". Wondering, if it makes sense to name the associated view model as "SelectMethodViewModel" or should it not use the action (i.e. Select) in the naming? I am thinking classes should be things so does the action not make sense or am I over thinking this? I know this is a simple question, but it has a trickle effect in the application.
I would definitely go ahead and use the same name for your View and ViewModel. In fact I would take it one step further and add View to SelectMethod.
This basically ensures that we know the relationship and purpose of the two classes.
TestView
TestViewModel
SelectMethod is not a good name for a view. A method is not a class, but a view is a class and so a view is not a method. I would suggest:
SelectionView
SelectionViewModel

Clarification of MVVM - interactions between views

I'm using WPF and trying to program the MVVM way.
I understand how every view has its own view model and this works quite well. I am struggling to manage the interaction between views though.
Say I have two views, View1 and View2, each with its own ViewModel, ViewModel1 and ViewModel2. If I have a combobox on View1 and a button, what is the correct way to close the first view, notify the second view of the selection and show the second view once the button is pressed? It doesn't seem like it should go in the model because it's a UI thing. The ViewModel shouldn't know how to open and close WPF forms (or should it?) And the views shouldn't know about any other ViewModels (or should they?)
So how are these problems solved? In a nutshell:
1) How is data passed between views?
2) What manages the lifetime/visibility of views?
It will depend on whether you are doing view model or view first, and the exact implementation details will depend on if you are using an MVVM framework. If you aren't using a framework, then I would strongly recommend you start using one.
In your example, when the button is pressed, a method on ViewModel1 will be invoked. If doing view model first (which I would recommend), you would instantiate an instance of ViewModel2, and at this point you could pass in the combobox selection to the constructor of ViewModel2.
Depending on your framework, there will be different ways of then displaying the view associated with ViewModel2.
For 1) you can sychronize data through the DataModel. Provided each view shares the same instance of the DataModel and it implements INotifyPropertyChanged multiple views can be updated simulateneously.
Your sesond question is a matter of design, as #devdigital states it can depend on whether it is View first or ViewModel first. I would consider the introduction of a Controller class in much as the same way ASP.Net MVC works which controls which view is displayed. You can expose a ViewClosed event on the ViewModel which the controller can listen to and based on your workflow open another view.
You might consider introducing Controllers which are responsible for the lifetime management of the ViewModels. Furthermore, they mediate between the ViewModels.
The sample applications of the WPF Application Framework (WAF) show how these Controllers can be implemented.

Can I link UIView controls (e.g. UILabel, UIPickerView) direct to model instance variables?

Background - Normally with basic UIViewControllers I'm across how you can create instance variables marked as IBOutlet's, and link these to the actual view controls with Interface Builder.
Question - If I pass a model object to a UIViewController, is there a way to link the View controls (e.g. UILabel, UIPickerView etc) directly to the model variables (i.e. effectively instance variables in a custom class)?
That is this way as soon as the user changes the UI control it would instantly update the model attributes. Assuming you've passed the model to the controller as pass-by-reference, therefore you've automatically updated the values for the parent as a result.
I'm thinking this would avoid having to (a) at initialisation translate the model values to set the UI components, and (b) when finishing up with the view having to manually update the model variables from the UI components.
Like onnoweb states, I don't think there's any way you could do this automatically - mainly because Obj-C would never know what types of variables you were actually looking for. However, you can (and I emphasis this), make your model control your view by letting your model have a view as a property.
Considering Obj-C is built on a MVC-architecture, I wouldn't advice it, though. Let your controller do the work. If you set triggers correctly, this shouldn't be a big problem.
I don't think Obj-C has the concept of property binding.