Suppose I have a model, Car, that is instantiated in ViewModel1 with the following initial properties:
ViewModel1
let car = Car(make: "McLaren", model: "P1", year: 2015)
Then I require additional information of the car to be completed in the next view controller. What is the correct way to pass a model between view controllers when following MVVM?
Using MVC, it is simple to do since the view can have reference to the model:
vc2.car = car
Below is a pseudo attempt at the problem, however I am under the impression that a view model should be private and only accessible to a single view controller. Therefore, the below attempt seems incorrect to me.
ViewController1
fileprivate let viewModel = ViewModel1()
func someMethod() -> {
let car = self.viewModel.car
let vc2 = ViewController2()
vc2.viewModel.car = car
present(vc2, animated: true, completion: nil)
}
ViewController2
let viewModel = ViewModel2()
func anotherMethod() {
print(self.viewModel.car.make) //prints "McLaren"
viewModel.manipulateCar() //adds additional information to the car object
print(self.viewModel.car.color) //prints "Black"
//Pass the finished car to the last view controller to display a summary
let vc3 = ViewController3()
vc3.viewModel.car = self.viewModel.car
}
I am wondering if what I have shown above is a fine way of doing things when using MVVM, or if not, what is the best way to pass the car between view controllers?
EDIT
This question does not have to do with Class vs Struct. The MVC example above implied it is going to be a class (since it is a reference) and it is being passed between multiple view controllers to complete more parts of the object.
It is a question of how to pass models between view controllers when following MVVM and whether view models should be private to the view controller.
With MVVM, the view controller should not reference the model, therefore should not have a variable var car: Car?
. Therefore, you should not see:
let vc2 = ViewController2()
vc2.car = car
Would it be incorrect to see this?
let vc2 = ViewController2()
vc2.viewModel.car = car
The other way of passing the data from Controller to View can be by passing an object of the model class to the View. Erase the code of ViewData and pass the object of model class in return view. Import the binding object of model class at the top of Index View and access the properties by @Model.
What most developers do to avoid the drawbacks of the MVC architecture is turn to the Model–View–ViewModel (MVVM) pattern. MVVM is better at separating logic and data, so it's a great choice to implement the thin controller, fat model concept.
MVVM (Model View ViewModel) is a design pattern, which has been widely used as more event-driven apps have started emerging. A few years back, if anyone built a product, it has probably been built using MVC (Model View Controller). In recent times, the MVC architecture has lost its place as the default design pattern.
This question has nothing to do with RxSwift, or even MVVM vs MVC. This is a Class vs Struct question. (Note that your comment "Using MVC, it is simple to do since the view can have reference to the model" is not correct when the model is a struct, because you can't pass references to structs.)
How you solve this problem is entirely dependent on how you transition from view controller to view controller.
When view controllers are in charge of the transition, each view controller would be in charge of making the next view controller and each view model would be in charge of making the next view model. Passing models back is done by having the "parent" view model listen to the "child" view model (through either a delegate, callback closure or reactive observable.)
View controllers can make the transition either through segues or "the old fashioned way" by creating and presenting the next view controller directly, or reaching up to it's container view controller (navigation VC for example) and telling it to make the transition.
A new trend in transitioning is to have a coordinator class take care of it instead of the view controllers. Using this idea, the coordinator holds the model, and creates view controllers as needed. The view models then talk back to the coordinator instead of (possibly creating and) talking to each other. This way, view controllers are independent of each other.
You can have the view models talk back to the coordinator using any of delegates, closure callbacks or Rx Observables.
Update from your edit:
You asked if it would be incorrect to have let vc2 = ViewController2();
vc2.viewModel.car = car
. The answer is yes, that would be incorrect, but close.
If view controllers are in charge of the transitions, then you would see is this instead:
// in view controller 1
let vc2 = ViewController2()
vc2.viewModel = self.viewModel.viewModel2
If you are using coordinators, then you would see something like:
// in coordinator
let vm2 = ViewModel(car: self.car)
let vc2 = ViewController2(viewModel: vm2)
The key idea behind the view model is not that it's private, it doesn't have to be. The key idea is that it is the only non-view object that the view controller holds on to. You can think of it as a "model controller."
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With