MVVM + Observables
Tags: swift, ios
Published: 2020-06-08 00:07:33

The previous post was a simple list displaying an array of strings.

Now, let's build up this project a little bit, shall we?   
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/v2bqebd5bo7be6oo7sw1.jpg)

# [Download the project](https://github.com/rfa1989/SwiftUIMVVM-GreatProducts/releases/tag/v1.1.0)
Download the project with the finished feature, and I'll explain what changed and how does it work now.

We had a pretty much basic folder structure in the first part, the initial project default layout:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xg4358yilrjt2p644d5i.png)

Now, there's a place for everything. What I did was to create a folder for each kind of class responsibility.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hgm61rn01ne349w1bmgc.png)

As we are using the MVVM pattern, we need at least:
* A **M**odel
* A **V**iew
* A **V**iew **M**odel

Using MVVM basically defines that:
* We have a model to store our model properties
* We have a view to display our model and receive user interaction
* We have a viewmodel that will handle user interaction coming from the view, and send model data to the view

---
*[search dev.to for more mvvm info](https://dev.to/search?q=mvvm)*

## The View

The project is still displaying a list of products, but now data is coming from the viewmodel.

```swift
struct ProductListView: View {
    //MARK: - view model
    @ObservedObject var viewModel = ProductListViewModel()
    
    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.productList) { product in
                    Text("Description: " + product.description)
                }
            }
            .navigationBarItems(trailing: Button(action: {
                self.viewModel.addNewProduct()
            }, label: {
                Text("Add new product")
            } ))
        }
        
    }
}
```

The first version we simply put our array of strings inside the view. But now, our view is referencing a variable inside the viewmodel class.

**Notice that the viewmodel is labeled as a `@ObservedObject`. This means that the view is observing the class, at any change of published variables these changes will reflect in our view.**

## The Model

```swift
struct Product : Identifiable {
    //MARK: - Properties
    
    var id = UUID()
    var description: String
}

extension Product {
    
    //MARK: - Default initialization
    init() {
        self.description = "New product"
    }
}
```

The model is pretty straightforward: 
* There's the description property where we can save the product description value
* and a default initialisation, so that we don't have to write property values every time we want to create a new product.

### The ViewModel


```swift
protocol ProductListViewModelProtocol {
    //MARK: - Protocol definition
    
    var productList: [Product] {get}
    func addNewProduct()
}

final class ProductListViewModel : ProductListViewModelProtocol, ObservableObject {
    //MARK: - Published properties
    
    @Published var productList: [Product] = []
    
    init() {
        productList.append(Product(description: "iPhone"))
        productList.append(Product(description: "Macbook Pro"))
    }
}

extension ProductListViewModel {
    //MARK: - Functions implementations
    
    func addNewProduct() {
        productList.append(Product())
    }
}
```

Here's where happens all the data and actions flow.
* We have a protocol to define the class capabilities
* A observable class, to enable this class be observed
* I also added a function `addNewProduct()` so that we can add a new product with the app running, and see the changes reflect in our view thanks to the observable feature.

And that's it.
Hit the play button, and now you'll see the same screen working, with an additional button on the right upper corner.
Click the button to add new products and enjoy.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/f8516syc2ua0vscjm9nv.gif)