Как view взаимодействует с viewModel в MVVM и как реализуется обновление состояния
Что проверяют
Понимаете ли вы направление потоков: View отправляет намерения во ViewModel, а состояние возвращается через наблюдение, без ссылки ViewModel на View. Следом обычно спрашивают, чем @StateObject отличается от @ObservedObject и как всё это выглядит в UIKit.
Как отвечать
Схема: View вызывает методы ViewModel (onAppear(), didTapRetry()), ViewModel меняет своё состояние, View перерисовывается. ViewModel не импортирует SwiftUI и не знает о View, поэтому тестируется без интерфейса.
Механизмы обновления:
@Observable(iOS 17+, фреймворк Observation): макрос делает свойства класса отслеживаемыми. View перерисовывается только при изменении тех свойств, которые прочитала вbody. Владелец хранит модель в@State, для привязок к полям есть@Bindable;ObservableObjectи@Published: нужны при поддержке iOS 16 и ниже. Любое изменение шлётobjectWillChange, и обновляются все подписанные View. Владелец держит модель в@StateObject, дочерним передаёт через@ObservedObjectили@EnvironmentObject;- UIKit: подписка через Combine,
viewModel.$state.receive(on: DispatchQueue.main).sink { ... }с хранениемAnyCancellable, либо замыкания и делегаты. Для@Observableвне SwiftUI естьwithObservationTracking.
Состояние экрана удобно держать одним enum: loading, loaded([Item]), failed(Error), а не тремя флагами, которые могут оказаться истинными одновременно. ViewModel помечают @MainActor, чтобы обновления UI шли в главном потоке, а в языковом режиме Swift 6 это ещё и способ пройти строгую проверку конкурентности.
Где спотыкаются
Пишут @ObservedObject var viewModel = MyViewModel(). SwiftUI пересоздаёт структуру View при каждой перерисовке родителя, вместе с ней создаётся новая модель, и состояние теряется. Создающая сторона должна использовать @StateObject.
Меняют @Published из фонового потока после сетевого запроса и получают предупреждение «Publishing changes from background threads is not allowed».
В sink захватывают self сильно, и контроллер с моделью не освобождается. Нужен [weak self] и хранение подписок в Set<AnyCancellable>.