Ответ
Model-View-ViewModel (MVVM) оптимален для современных iOS-проектов со сложным UI и бизнес-логикой, особенно с использованием реактивного программирования.
Основные причины для выбора MVVM:
- Сложные, динамические интерфейсы – где состояние View часто меняется в ответ на данные (например, live-поиск, валидация форм).
- Независимое тестирование бизнес-логики – ViewModel не содержит ссылок на View, что позволяет писать чистые юнит-тесты.
- Использование реактивных фреймворков – таких как Combine (нативный) или RxSwift. Связь «данные -> UI» через биндинги (
@Published,ObservableObject). - Крупные проекты с командной работой – четкое разделение позволяет параллельно работать над логикой и интерфейсом.
Пример с использованием Combine:
// ViewModel
class UserListViewModel: ObservableObject {
@Published var users: [User] = []
@Published var isLoading = false
@Published var errorMessage: String?
private let service: UserService
private var cancellables = Set<AnyCancellable>()
init(service: UserService) {
self.service = service
}
func fetchUsers() {
isLoading = true
service.fetchUsers()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { [weak self] completion in
self?.isLoading = false
if case .failure(let error) = completion {
self?.errorMessage = error.localizedDescription
}
}, receiveValue: { [weak self] users in
self?.users = users
})
.store(in: &cancellables)
}
}
// View (SwiftUI - идеально для MVVM)
struct UserListView: View {
@StateObject var viewModel: UserListViewModel
var body: some View {
List(viewModel.users) { user in
Text(user.name)
}
.overlay {
if viewModel.isLoading { ProgressView() }
}
.alert("Ошибка", isPresented: .constant(viewModel.errorMessage != nil)) {
Button("OK") { viewModel.errorMessage = nil }
} message: { Text(viewModel.errorMessage ?? "") }
.onAppear { viewModel.fetchUsers() }
}
}
Для простых экранов MVVM может быть избыточен.