A Practical Guide to MVVM Architecture in SwiftUI A Practical Guide to MVVM Architecture in SwiftUI

SwiftUI, Apple's modern framework for building user interfaces, has brought a fresh approach to iOS app development. When combined with the Model-View-ViewModel (MVVM) architectural pattern, it allows for creating clean, modular, and testable code. In this tutorial, we will explore how to implement MVVM in a SwiftUI application step by step.

Define the Model

In MVVM, the Model represents the data and business logic of your application. For this tutorial, let's create a simple Model to manage a list of tasks.

import Foundation

struct Task: Identifiable {
    let id = UUID()
    var title: String
    var completed: Bool
}

class TaskManager: ObservableObject {
    @Published var tasks: [Task] = []

    func addTask(_ title: String) {
        let task = Task(title: title, completed: false)
        tasks.append(task)
    }
    
    // Add other methods for managing tasks
}
Build the ViewModel

The ViewModel serves as the bridge between the Model and the View, providing data and handling user interactions.

import Foundation
import SwiftUI

class TaskViewModel: ObservableObject {
    @Published private var taskManager = TaskManager()
    @Published var newTaskTitle = ""
    
    var tasks: [Task] {
        taskManager.tasks
    }

    func addTask() {
        guard !newTaskTitle.isEmpty else { return }
        taskManager.addTask(newTaskTitle)
        newTaskTitle = ""
    }
    
    // Add other ViewModel logic here
}
Create the View

In SwiftUI, the View is responsible for displaying data and capturing user interactions. Create a View that displays the list of tasks and allows the user to add new ones.

import SwiftUI

struct TaskListView: View {
    @ObservedObject var viewModel: TaskViewModel
    
    var body: some View {
        NavigationView {
            VStack {
                TextField("Add a new task", text: $viewModel.newTaskTitle)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .padding()
                
                Button(action: viewModel.addTask) {
                    Text("Add Task")
                }
                .padding()
                
                List(viewModel.tasks) { task in
                    Text(task.title)
                }
            }
            .navigationTitle("Task List")
        }
    }
}
Connect the View and ViewModel

In your app's entry point (e.g., App.swift), create an instance of the TaskViewModel and inject it into your view.

import SwiftUI

  @main
  struct MVVMTutorialApp: App {
      @StateObject var viewModel = TaskViewModel()
  
      var body: some Scene {
          WindowGroup {
              TaskListView(viewModel: viewModel)
          }
      }
}
Testing and Running the App

Build and run your project. You should be able to add tasks, and they will appear in the list. The text field should clear after adding a task.

Conclusion

You've successfully implemented MVVM architecture in a SwiftUI application. MVVM encourages separation of concerns, making your code more maintainable and testable. You can extend this example by adding more features, incorporating data persistence, or even using Combine to handle asynchronous operations. MVVM in SwiftUI is a powerful combination for building robust and scalable iOS applications.