Что такое паттерн MVVM?

Ответ

MVVM (Model-View-ViewModel) — это архитектурный паттерн, который разделяет ответственность в приложении на три основных слоя для улучшения тестируемости и поддержки кода.

Компоненты:

  1. Model: Представляет данные и бизнес-логику. Не зависит от UI.
  2. View: Отображает пользовательский интерфейс (UIViewController, UIView, SwiftUI View). Его задача — отражать состояние ViewModel и передавать пользовательские действия.
  3. ViewModel: Посредник между Model и View. Он:
    • Преобразует данные из Model в удобный для отображения формат.
    • Содержит логику представления (например, форматирование строк).
    • Реагирует на действия пользователя из View, обновляя Model.

Ключевой принцип: View связывается (binds) с ViewModel, обычно реактивно (через Combine, RxSwift или замыкания), чтобы автоматически обновляться при изменении состояния ViewModel.

Пример на SwiftUI и Combine:

// MARK: - Model
struct User {
    let name: String
    let registrationDate: Date
}

// MARK: - ViewModel
import Combine

final class UserViewModel: ObservableObject {
    @Published var displayName: String = ""
    private let user: User
    private let dateFormatter: DateFormatter

    init(user: User) {
        self.user = user
        dateFormatter = DateFormatter()
        dateFormatter.dateStyle = .medium
        configureDisplayName()
    }

    private func configureDisplayName() {
        let formattedDate = dateFormatter.string(from: user.registrationDate)
        displayName = "(user.name) (joined: (formattedDate))"
    }
}

// MARK: - View (SwiftUI)
struct UserView: View {
    @StateObject var viewModel: UserViewModel

    var body: some View {
        Text(viewModel.displayName) // View автоматически обновится при изменении `displayName`
    }
}

Преимущества MVVM:

  • Разделение ответственности: View становится "тупым" и занимается только отображением.
  • Высокая тестируемость: ViewModel можно протестировать юнит-тестами без необходимости в UI.
  • Упрощение ViewController: Логика "уходит" из массивных ViewController.

Ответ 18+ 🔞

Давай разберём эту вашу MVVM, а то народ путается, как хуй с пальмой. Архитектурный паттерн, блядь, звучит страшно, а на деле всё просто, как три рубля.

Смотри, есть три главных героя в этой истории, и если их не разделить — будет пиздец, как в тех ViewController'ах на тысячу строк, где и бизнес-логика, и анимации, и сетевые запросы, и ещё бог знает что. Там и черти ногу сломают.

Кто есть кто:

  1. Модель (Model). Это священная корова, данные и правила. Не знает ни о каком интерфейсе, живёт в своём мире. Например, struct User { name: String }. Её дело — хранить и валидировать. Всё.

  2. Вью (View). Это то, что тыкает юзер. Кнопки, текст, свитчи. Его задача — выглядеть красиво и тупо показывать, что ему сказали. Вся его логика: "нажали на кнопку — сообщил об этом куда надо". Мозгов у него — ноль ебать. И это правильно.

  3. ВьюМодель (ViewModel). А вот это, сука, самый главный распиздяй и работяга. Он — посредник. С одной стороны, он дружит с Моделью, вытаскивает из неё сырые данные. С другой — он эти данные облизывает, причёсывает, делает красивыми для тупой Вьюхи. Например, берёт Date из модели и превращает в строку "Зарегистрирован 25 мая". Вся логика форматирования, фильтрации — здесь.

Магия связи (Binding): Вот тут самое интересное. Раньше, в каменном веке, ВьюМодель кричала Вьюхе: "Эй, обновись, у меня данные поменялись!". Теперь же делают хитро: Вью подписывается на ВьюМодель. В SwiftUI это @Published свойства и @StateObject. Изменилось что-то в ВьюМодели — Вьюха сама, автоматически, как хитрая жопа, перерисовывается. Никаких ручных tableView.reloadData(). Красота!

Смотри, как это выглядит в коде (SwiftUI + Combine):

// MARK: - Модель (Просто данные, блядь)
struct User {
    let name: String
    let registrationDate: Date
}

// MARK: - ВьюМодель (Тут вся работа)
import Combine

final class UserViewModel: ObservableObject {
    @Published var displayName: String = "" // За этим свойством Вьюха будет следить
    private let user: User
    private let dateFormatter: DateFormatter

    init(user: User) {
        self.user = user
        dateFormatter = DateFormatter()
        dateFormatter.dateStyle = .medium
        configureDisplayName() // Готовим данные для показа
    }

    private func configureDisplayName() {
        // Превращаем сырую дату в красивый текст
        let formattedDate = dateFormatter.string(from: user.registrationDate)
        displayName = "(user.name) (зареган: (formattedDate))"
    }
}

// MARK: - Вью (Тупая, но красивая)
struct UserView: View {
    @StateObject var viewModel: UserViewModel // Подписались на ВьюМодель

    var body: some View {
        Text(viewModel.displayName) // Просто показываем то, что дали. И всё!
        // Если в viewModel.displayName что-то изменится — этот текст сам обновится. Волшебство, ёпта!
    }
}

И зачем этот цирк, спросишь?

  • Тестировать можно без истерик. ВьюМодель — это просто класс. Ты можешь её взять и протестировать юнит-тестами, не открывая симулятор и не тыкая в интерфейс. Это овердохуища удобно.
  • ВьюКонтроллеры перестают быть свалкой. Вся логика уезжает из них в ВьюМодель. ВьюКонтроллер становится легким и пушистым, только показывает вьюхи и жесты обрабатывает.
  • Переиспользование. Одну ВьюМодель могут использовать разные Вью (например, для iPhone и iPad). Или одну Вью можно подключить к разным ВьюМоделям.

Короче, MVVM — это не просто мода. Это способ не сойти с ума, когда проект растёт. Сначала кажется, что слоёв дохуя и всё сложно, но потом привыкаешь и понимаешь — жить без этого пиздеца уже не можешь.