Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS: How to pass a model from view model to view model using MVVM?

Tags:

ios

swift

mvvm

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
like image 801
smandrus Avatar asked May 06 '17 00:05

smandrus


People also ask

How do you pass data into a ViewModel?

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.

Is MVVM better than MVC iOS?

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.

Can you explain MVVM and how it might be used on Apple's platforms?

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.


1 Answers

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.

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.

When coordinators are in charge of 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."

like image 160
Daniel T. Avatar answered Nov 15 '22 23:11

Daniel T.