From the course: SwiftUI Essential Training

MVC versus MVVM

- [Instructor] If you've worked with UIKit and Xcode, you're familiar with the MVC pattern. If you're not, let me explain. UIKit and much of mobile device programming uses the MVC pattern to organize your code into three functions. The model is the code that handles your data. The view shows and accepts inputs from the user. The controller handles the interactions between the two and where you as a developer do most of the front-end work. UIKit tends to combine the view and controller portion into a view controller class where you either write the view programmatically or use a storyboard. For simplicity, I'm going to take them apart into a simple toggle button example. The button would be the view and we'd have some Boolean value, which we'd use as our model. The controller would be some code between the two that when a button is pressed would change the value and the color of the button. In SwiftUI we use a variation of MVVM where there's no controller, just models and views. Sometimes referred to as reactive programming, it changes the data, force a redraw of the entire view and any subviews. For our button example, I might press the button. The view reacts by changing the value of the model. A change in this value triggers a redraw of the button and there is a change in the button's color based on the value of color found in the modifiers of the button. In short, the model forces changes to the view when it changes, often due to another change of a view. Models become very important for a dynamic application. We've yet to add a model for data to our application. Since this is so different than what you may be used to, let's look at this more structurally before we're looking at some code. Up to now, we've passed data from one struct to another through parameters. So from MenuView to MenuRowView, we passed data along through its parameter. Now, down through the hierarchy is easy, but changing of value or sending it up is not so easy. Structs are value types, and even a variable is difficult to change and you'll get an error message like this. To make such changes, we use a variable in a State wrapper. State wrappers let us have changes to variables. Any change to a State variable also triggers a redraw of the view. Going back to our button, I can define a State variable, color, and when it changes, I use a conditional operator to pick a color. Note that as a variable in a struct, this is also the parameter of the struct, so I can a set of value from the super view into my ButtonView. However, State is local to the struct, and often, private. We may want to send the State to the super view of our current view. To do that, we use a Binding wrapper. Binding has a similar declaration to State, but has one more wrinkle, you must have your types wrapped in Binding or State to pass them as parameters. For example, let's say make a ButtonView like I did before, but I changed the State to a Binding. I have a view that calls this and has a parameter that is either a Binding or State variable. I mark this with a dollar sign in my code. The code then passes up the hierarchy that value and the super view reacts by changing the background. Both State and Binding mean we pass values through the hierarchy. However, some models we may want more direct access to a specific view. For those, we have the ObservedObject. In defining the model, we tell the SwiftUI compiler to watch this model, and if there's a change where used, trigger the redraw and update the views. With these three ways of defining models, we can make SwiftUI work effectively to change our view when we need to. In the next few chapters, we'll look at the code you need to use State, Binding, and ObservedObjects and some important quirks when using them.

Contents