Пример архитектуры MVVM на UIKit + Callback

При разработке приложений для iOS одним из ключевых аспектов является выбор правильной архитектуры. Одной из самых популярных и эффективных архитектур является Model-View-ViewModel (MVVM). Она помогает разделить ответственность между компонентами, улучшает тестируемость и позволяет легче управлять сложными интерфейсами. В сочетании с использованием callback'ов в UIKit, MVVM становится мощным инструментом для разработки приложений, обеспечивая отзывчивый и интуитивно понятный пользовательский интерфейс.


В этой статье мы разберем пример использования архитектуры MVVM в связке с UIKit и callback'ами. Вы увидите, как эти концепции работают вместе, упрощая разработку и поддержку приложений.

Что такое MVVM?

Архитектура MVVM разделяет логику приложения на три части:

  • Model — данные и бизнес-логика.
  • View — пользовательский интерфейс и его элементы.
  • ViewModel — слой, который связывает Model и View, обрабатывает данные и предоставляет их для отображения в UI.

Основная идея заключается в том, что ViewModel управляет состоянием View, получая данные из Model, форматируя их для отображения и взаимодействуя с UI через привязки данных (binding). При использовании UIKit привязка данных реализуется через callback'и, замыкания или уведомления.

Пример MVVM на UIKit

Рассмотрим простой пример приложения на UIKit с использованием MVVM и callback'ов. Приложение будет отображать список задач (To-Do list), загружая данные из модели, и обновлять UI по мере изменений.

Model

Модель содержит данные о задачах и логику их загрузки. В данном примере данные будут загружаться асинхронно, и после завершения операции вызовется callback.

struct Task {
    let id: Int
    let title: String
}

class TaskService {
    func fetchTasks(completion: @escaping ([Task]) -> Void) {
        // Имитация асинхронной загрузки данных
        DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
            let tasks = [
                Task(id: 1, title: "Купить продукты"),
                Task(id: 2, title: "Выучить MVVM"),
                Task(id: 3, title: "Сделать зарядку")
            ]
            completion(tasks)
        }
    }
}

ViewModel

ViewModel служит промежуточным слоем между Model и View. Он получает данные от TaskService и преобразует их для отображения в UI.

class TaskViewModel {
    private let taskService: TaskService
    var tasks: [Task] = []

    // Callback для обновления View
    var onTasksUpdated: (() -> Void)?

    init(taskService: TaskService) {
        self.taskService = taskService
    }

    func loadTasks() {
        taskService.fetchTasks { [weak self] tasks in
            self?.tasks = tasks
            self?.onTasksUpdated?() // Вызываем callback после получения данных
        }
    }

    func numberOfTasks() -> Int {
        return tasks.count
    }

    func taskTitle(at index: Int) -> String {
        return tasks[index].title
    }
}

View (UIKit)

View — это UI-элементы, которые взаимодействуют с пользователем. Здесь мы используем UITableView для отображения списка задач, и с помощью callback'а обновляем UI после загрузки данных.

import UIKit

class TaskListViewController: UIViewController {
    private let tableView = UITableView()
    private let viewModel: TaskViewModel

    init(viewModel: TaskViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()

        // Привязываем callback для обновления View
        viewModel.onTasksUpdated = { [weak self] in
            DispatchQueue.main.async {
                self?.tableView.reloadData()
            }
        }

        // Загружаем задачи
        viewModel.loadTasks()
    }

    private func setupUI() {
        view.addSubview(tableView)
        tableView.frame = view.bounds
        tableView.dataSource = self
    }
}

extension TaskListViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.numberOfTasks()
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
        cell.textLabel?.text = viewModel.taskTitle(at: indexPath.row)
        return cell
    }
}

В этом примере ViewModel вызывает замыкание onTasksUpdated, когда данные загружены, а View (в нашем случае TaskListViewController) привязывается к этому замыканию для обновления интерфейса.

Преимущества использования MVVM и callback'ов

  1. Разделение ответственности: Четкое разделение между слоями Model, View и ViewModel упрощает поддержку и расширение кода.
  2. Тестируемость: ViewModel легко тестировать, так как он не зависит от UIKit, а только от Model.
  3. Улучшение производительности: Callback'и позволяют обновлять интерфейс только по завершении асинхронных операций, что улучшает отзывчивость приложения.

Заключение

Архитектура MVVM в сочетании с callback'ами на UIKit — это мощный инструмент для построения асинхронных и отзывчивых приложений. Она помогает структурировать код, улучшить его поддержку и сделать его более понятным и тестируемым. При правильном использовании MVVM позволяет достичь высокой гибкости и масштабируемости, что делает ее отличным выбором для разработки сложных приложений на iOS.