How to Keep state of my View Model persistent across a user session in SwiftUI (MVVM)? - 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.

Related

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

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.

What should a view contain in an MVC?

I am currently working with Swift and I was learning about MVC - one question that popped out was this: I am trying to implement a WKWebView - and I already know how to do this within a ViewController.
My question is: should I create my own WebView class to place the WKWebView or should I only house it with in the ViewController? I am trying to follow the MVC structure.
It's a web view, so it should be placed in the view controller in the storyboard. This really has nothing to do with MVC, per se.
The view controller is your Controller.
The view controller's root view and the web view you're putting in it are the View.
The Model may or may not be relevant here since a model is usually just a data structure that the Controller uses to populate the View. It could just be the HTML that you pass to the web view.
My question is: should I create my own WebView class to place the WKWebView or should I only house it within the ViewController? I am trying to follow the MVC structure.
The view in Model View Controller really refers to a view and its entire graph of subviews. If a WKWebView instance is the view that contains everything that the controller will manage, then it's fine to make that "the" view; there's no need to put it inside another view just for the sake of containing it. On the other hand, if you want the same controller to manage other views not contained in the web view, then you can put the web view and the others all inside some other view and let the controller manage that.
How you organize your views really isn't determined by MVC -- just do what works. MVC really speaks to the way that the information your app operates on is owned and managed by a model, displayed in a view, and how the interactions between model and view are mediated by the controller.

How can we get view object outside it's own controller in sap ui5? and how can we access the controls of a view outside it's controller in sap ui5?

Suppose I have a view
How can I get a control of this view by Id from a different controller.
I have tried to set up an ID for the view and tried to access the view by
sap.ui.getCore().byId("mainViewID") in another controller but it didnt work.
It took only the dynamic view id what you can find from the html elements.
Getting a control from another view is discouraged and is generally indicative of bad design.
In case what you need to do is access data in another view or to change a property of a control based on user input, the better method is to bind those properties to a model and then access that model from the second view.
You can do this by attaching the model to the core, thereby making it accessible throughout the application. For example:
var oModel = new sap.ui.model.json.JSONModel();
this.getCore.setModel(oModel,"modelName");
Alternatively, you can create this model in the manifest file.

How to create a segue performing a transition to the same view with another model object?

I am writing an iOS / CocoaTouch app and I am facing the following problem :
I have a detail view (think of an overview of one given object)
This detail view can present other elements
Any of these other elements can be viewed in this exact same detail view (I mean, another instance of this view / view controller using the viewed object model.
The only problem I have is that I am not able to create a segue from a view to the same view in the storyboard editor. Therefore, I cannot create the segue at all, cannot assign an identifier, and thus cannot trigger it from code.
Is there any way to implement this ?
This is as simple as a detail view pushing another, each of them having one dedicated instance of the view controller with their respective object model.
Thank you so much, I looked everywhere and cannot find any topic related to this.
Christophe.
Segues are between view controllers, not views (even though a view can act as a trigger). If you want to have a segue to a new view controller, create a new instance of it in the storyboard, assign its identity to the same class as your original detail, and define the segue.
If you're only trying to change which view is displayed inside a single view controller, then selectively setting views hidden and not-hidden can work...or adding/removing sub-views.

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.