I read a lot of blog posts about MVVM-C (Swift, especially), and always a navigation is triggered View => ViewModel => Coordinator.
Wouldn‘t it be easier to have a reference between View(Controller) and Coordinator to avoid the intermediate step with the ViewModel? What‘s the reason the View(Controller) must not know about the Coordinator?
I agree with above answer, but would like to add some more reasoning:
Following Single responsibility from SOLID, ViewController which is View in MVVM should not be responsible for screen transitions and notifying of Coordinator that a transition should occur.
Screen transition is a part of business logic. So it can be triggered in ViewModel and that is a good place for it.
Related
My situation is, I have a navigation controller(nv) with root view controller(rootVC). And another view controller(firstChildVC) pushed to rootVC. And one more view controller(secondChildVC) pushed to firstChildVC. (In real case, I have more subsequence child view controllers) After API calls and some calculations in secondChildVC, I need to pass some data from secondChildVC back to rootVC and popToRootViewController to show some data.
I don't think delegate and closures are good choice in this case. The only thing I could come up with is using NotificationCenter. Just what to know is there any better way to do this?
Thank you in advance.
You can try this inside the SecondVC then pop to root
if let root = self.navigationController?.viewControllers.first as? RootVC {
root.sendData(data)
}
I think there are three valid ways to do this without overly tightly coupling things:
Notification Center
Closures
Delegate pattern
The right way is not a simple choice. It really depends on the details. You mention making an API call. Assuming this is all managed in a separate object and not coded into your view controller code, I would have the object making the API call post a notification and the view controllers can each listen and do whatever is appropriate.
There are two other ways to do this, one is using Delegates another one is Closures
What I would like to do is unsubscribe from events at the moment the ViewModel is no longer needed. I tried implementing IDisposable but nobody calls Dispose(), not Xamarin.Forms nor Prism.Forms.
We have an app created with Xamarin.Forms. We use Prism.Forms to do MVVM. When navigating to a new page (push on stack) Prism.Forms wires the ViewModel to the Page. When navigating back (pop from stack) the ViewModel gets GarbageCollected after a while.
The problem is however that at a point in time we have a couple of the same type of ViewModels with subscriptions to events that are not bound to a View. When the events fire all these ViewModels start doing their thing. So I am looking for a way to unsubscribe at the moment the subscription is no longer needed.
Does anyone have a solution?
You can make sure the Dispose() is called in the OnDisappearing() event of the View, if you want to ensure that ViewModel is not present anymore in the memory than the view.
It is better if you care only about subscribe and unsubscribe of events, then to do it in the OnAppearing() and OnDisappearing(). In that case you will be sure of no event handlers been present on the viewmodel once view is not visible.
Implement IDestructible or INavigationAware
(or both as in BaseViewModelsample from Prism).
Depending on your object lifecycle :
Implement your disposal code in the Destroy method of IDestructible interface.
Implement your disappearing/appearing code in the OnNavigatedFrom/OnNavigatedTo methods on INavigationAware interface.
Bonus:
IDestructible can be implemented also by the View (and it will called accordingly by Prism when the view is destroyed).
Note:
While the solution above using OnAppearing/OnDisappearing works, it induces that ViewModel will depends on a call from View for managing its lifecycle (not clean). Moreover these methods don't exist on ContentView.
i have a class RequestHandler that takes the requests for some ViewController and fetches data on the web asynchronously. In order to notify the ViewController, it implements a protocol and the ViewController is set as its delegate.
Now, this ViewController is a TableViewController, and when a row is selected, it pushes a second ViewController on the NavigationStack. This second (child) ViewController needs to use the RequestHandler too. How can i make it a delegate for the same RequestHandler instance? And how can i make sure it won't mess with the parent TableViewController once i go back to it?
The fact that both view controller would want the same request suggests a design error. The view controllers should display the current state of the Model. They should not directly deal with active network requests.
You should have some group of classes that represent your data. These are called the Model. View controllers should only care about the model while the view is onscreen. So a reasonable pattern looks like this:
ViewController registers for notifications of changes in the Model
ViewController updates view with current data from Model.
ViewController requests an update.
RequestManager (singleton) creates a new RequestHandler to process it.
When RequestHandler finishes, it tells RequestManager and is released.
RequestManager updates Model with new data
Model alerts registered observers that it has changed.
ViewController updates view with current data from Model.
Now it doesn't matter if the user is on this view, or has moved to another, or moves to another and comes back. In call cases, any time the model changes, the current view is updated.
If I understand you correctly, is the RequestHandler a class written by you, so you could allow it to take more than one delegates for the implemented protocol (just store the delegates in an NSMutableArray, so you can add reps. remove them as you need)
Now when the new view is created, you can just 'register' it to your RequestHandler. The same way if the view is going to be closed, you could/should deregister it from your RequestHandler.
I'm new to Objective-C and not a full time programmer. I'm beginning to understand the Model-View-Controller design pattern for differentiating the UI from the model. So the user takes an action and the view controller sends a message to the delegate (model). But I'm not sure what the best way to send actions from the delegate back to the view controller.
For example, the user pushes a button, the VC messages the Delegate. That part I understand. Then the delegate takes action, and following that the delegate wants to update the VC (e.g., update a label).
So what I missed (or have forgotten) is how this gets done, while maintaining separation between the UI and the model. I suppose I can use the notification center. Or I think I can just have the view controller pass a callback to the delegate. Or maybe there's another choice I don't know of. Can someone give me a recommendation, please?
I think you're slightly misunderstanding the MVC paradigm. Models should never be delegates of views, since models should have no dependencies or knowledge of any view classes. Typically, a view sends a message to its delegate or target (if you're using target/action), which is usually a controller (often a subclass of UIViewController on iOS). The controller then accesses data from the model and can update any views that need updating. I'd recommend reading the MVC fundamentals guide for a more complete explanation.
Basically you're right, you could do all the notification-related things yourself (i.e. with NotificationCenter) but since we're talking about UI-Stuff here I would greatly recommend you to use IBAction-Methods and IBOutlet-Properties in your code which you can easily connect to UI-Elements respectively their Callbacks in Interface Builder.
A very basic introduction to this topic can be found here:
iPhone SDK Interface Builder basic training
i hope that it is not too basic tough, and that I could lead you on the right track.
First of all delegate is NOT a Model.
Model is something passive that only holds the data (DB, plist, array, dictionary etc.).
While delegate is some set of functions that exist in order to react to some events.
Delegate is more likely to be a view controller in your case.
The view controller should react to user's action.
If the button tap should display some data from your model in some label then view controller should do all the work (receive user's action, take the necessary data from the model and display it on the view...).
We are building an app using the MVVM pattern, we have controllers that wire up all the views and viewmodels using DI. All examples of MVVM I've seen are really simplistic and have 1 view. How do/should viewmodels talk back to the controller? The controller knows about the models and views, should the viewmodel send events back to the controller? Where should a save happen? Model? Controller?
Could your ViewModel not take a dependency on an IController or some other interface, so they can talk back to it? I try to keep as much application logic out of the ViewModel as possible, as these classes can easily become bloated.
MyViewModel(IController controller)
{
this.controller = controller;
}
void Save()
{
this.controller.Save();
}
I do agree that the MVVM frameworks tend to be too simplistic with their samples. In particular, moving between views/screens in your application is something I would like to see more examples of. I create an IViewManager interface, to allow my ViewModels to request that we move to another view.
We use Controllers too but in our case they are responsible for the application workflow. The Controller knows the ViewModel and the Model but not the concrete View because this will be injected by the IoC Container.
If you are interested in an example that shows more than just one UI (modal dialog, wizard with conditional workflow) then you might have a look at:
WPF Application Framework (WAF) - http://waf.codeplex.com
In case of an application that has multiple modules and requires separation of concerns I would recommend using prism framework.
http://msdn.microsoft.com/en-us/library/gg406140.aspx
I use a similar setup to you. In my controller, where my DI and view injection goes down, I sometimes keep reference to the ViewModel (which hold the View). Some cases I may have an event on the VM which is handled by the controller. In other, extreme cases (like if the VM/V was created outside the controller, say in another VM), I may even use the EventAggregator (with a strong ref) to listen to events that may be fired on the VM. In that case, a stored ref to the VM is not needed.
How about using events wherein the controller subscribes to VM events or using a mediator pattern where in a mediator is injected in a VM.