Simplify model MVVM + SwiftUI

Tram Ho

1. Overview

SwiftUI is a framework that gives us the tools to build user interfaces on different platforms, including iOS, macOS, and watchOS. Applications built on SwiftUI can use a number of different architectures depending on the requirements and size of the project (MVVM, Redux, IVO…). However, the MVVM architecture is one that is recommended by Apple and is widely used in SwiftUI projects.

So, in today’s article, let’s learn about this architecture in a very simple way. Let’s do it!!!!

2. Relationship between components in MVVM . model

mvvm-swiftui-architecture.png

  • Model has the following tasks:
    • Define data structures mapped from Local Database (stored on device: Realm, CoreData, SQLite, … ) or Remote Database (API, …)
    • Interacting with Data (CRUD)
  • ViewModel :
    • The ViewModel contains the application’s business logic, usually implemented as an ObservableObject containing the @Published properties .
    • This layer can listen for changes from the Model and expose those changes to the View .
  • View :
    • View is responsible for defining the structure of the UI (View, Text, Button, …), animation, etc.
    • The View binds to the ViewModel via @ObservedObject so it can listen for changes in response to the ViewModel .
    • In addition, the View is also responsible for receiving interaction events from the user to transmit to the ViewModel to handle logic

3. MVVM implementations with SwiftUI

Together we will implement MVVM architecture combined with SwiftUI simply through the example below

3.1 Creating a Model

  • Create struct Item to define data structure of data (can be from API or Local Database)
  • Create a class ItemStore that is responsible for calling data with the fetchItems() method and saving the data to the items property

Note: the items property in the ItemStore class is wrapped by @Published so that the ViewModel can listen for data changes from the Model layer.

(here is the Item list retrieved from the API and stored in the items variable)

3.2 Creating a ViewModel

  • Step 1 : We create an ItemViewModel class that conforms to the ObservableObject protocol .
  • Step 2 : We create the items variable whose data type is the Item array and wrapped by @Published .
  • Step 3 : initialize the itemStore object from the ItemStore class (this class is defined at the Model layer) which is responsible for interacting with the data at the Model layer
  • Step 4 : create a variable cancellables is a Set of AnyCancellable to help us manage subscriptions and ensure that these subscriptions will be canceled when not needed. ( The subscription in this example is itemStore.$items )
  • Step 5 (last step) : in the init function of the ItemViewModel class, we assign items in the ItemViewModel class with the data after being called from the API at the Model layer.

3.3 Creating a View

On the View layer, we first create an ItemListView that conforms to the View protocol, then inside the ItemListView struct we create a viewModel variable whose data type is ItemViewModel and wrapped by @ObservedObject . This is to ensure that every time the viewModel changes, the View can fully listen.

Explain the behavior for the above example:

  • First, when initializing ItemListView and viewModel the list of items under the ViewModel will be empty -> ie the View will also display an empty list.
  • When ItemListView appears ( Appear ) fetchItems() function on ViewModel layer will be called -> after a period of time, when receiving data [Item] from API returned, ItemListView will be displayed with a list with quantity elements equal to the number of elements present in the API response.

In this example, because we mock and immediately return an Item array in the fetchItems() function, our eyes do not notice the difference in the data loading process. In real-life situations, sometimes our network is slow or our query is complex, this difference will be obvious.

3.4 Unit Test

Writing Unit tests in real applications is extremely important. Here’s how to implement Unit test ViewModel of a SwiftUI app incorporating MVVM pattern.

In the above example, when the testFetchItems() function is called, the items list will be assigned by 2 elements [Item(name: “Item 1”), Item(name: “Item 2”)] . So we write test_fetchItems() function to check if after calling testFetchItems() the list of items actually has data or not.

Hate!!! Very easy.

4. Summary & References

  • Through this article, we have learned together the components of the MVVM architectural model and how to implement the MVVM architecture with the SwiftUI framework. If you find the article interesting and useful, please give me an upvote and follow to give me more motivation to write
  • Send you the reference code: https://github.com/thuannv-1/simple-mvvm-swiftUI
Share the news now

Source : Viblo