В каком виде презентер получает данные?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Как презентер получает данные в архитектуре VIPER?
В архитектуре VIPER презентер выступает центральным координатором между модулями, отвечающим за бизнес-логику и подготовку данных для отображения в View. Он получает данные через четко определенные механизмы взаимодействия с другими компонентами, преимущественно через Interactor и Entity, но также может получать данные напрямую от View или через Router в определенных сценариях.
Основные источники данных для презентера:
1. От Interactor через протоколы (контракты)
Это основной и наиболее чистый способ. Презентер декларирует свои потребности через Input Protocol, а Interactor реализует Output Protocol, по которому передает результаты.
// Протокол для Interactor -> Presenter
protocol ListInteractorOutputProtocol: AnyObject {
func didFetchUsers(_ users: [UserEntity])
func didFailToFetchUsers(with error: Error)
}
// Протокол для Presenter -> Interactor
protocol ListInteractorInputProtocol {
func fetchUsers()
}
// Presenter реализует выходный протокол Interactor
class ListPresenter: ListInteractorOutputProtocol {
weak var view: ListViewInputProtocol?
var interactor: ListInteractorInputProtocol?
func fetchData() {
interactor?.fetchUsers() // Запрос данных
}
// Получение данных от Interactor
func didFetchUsers(_ users: [UserEntity]) {
let viewModels = users.map { UserViewModel(from: $0) }
view?.displayUsers(viewModels) // Передача подготовленных данных в View
}
func didFailToFetchUsers(with error: Error) {
view?.displayError(message: error.localizedDescription)
}
}
2. От View через события пользователя
Презентер получает данные в виде пользовательских событий или действий, которые View передает ему через свой Input Protocol. Это не "данные" в виде моделей, а триггеры для их получения.
// View сообщает Presenter о действии пользователя
protocol ListViewOutputProtocol {
func viewDidLoad()
func userDidSelectItem(at index: Int)
func userDidSearch(with query: String)
}
class ListPresenter {
func userDidSearch(with query: String) {
// Presenter может либо передать query Interactor для фильтрации,
interactor?.filterUsers(with: query)
// либо выполнить простую логику самостоятельно.
}
}
3. От Router при навигации
В некоторых случаях Router может передавать презентеру начальные параметры или конфигурацию при создании модуля.
class ListRouter {
static func assembleModule(with initialData: SomeInitialConfig) -> UIViewController {
let presenter = ListPresenter()
presenter.initialConfiguration = initialData // Данные переданы напрямую
// ... остальная сборка модуля
return viewController
}
}
4. Прямая обработка и трансформация Entity
После получения "сырых" Entity от Interactor, презентер выполняет их трансформацию в ViewModels (или другие структуры), удобные для View. Это его ключевая ответственность.
struct UserEntity {
let id: Int
let firstName: String
let lastName: String
let registrationDate: Date
}
struct UserViewModel {
let fullName: String
let registrationYear: String
init(from entity: UserEntity) {
self.fullName = "\(entity.firstName) \(entity.lastName)"
let year = Calendar.current.component(.year, from: entity.registrationDate)
self.registrationYear = "Registered in \(year)"
}
}
Ключевые принципы получения данных:
- Через протоколы: Все взаимодействие строго типизировано и инкапсулировано.
- Декомпозиция ответственности: Interactor получает "сырые" данные из сети/базы, Presenter их готовит для отображения.
- Реактивная или блочная модель: Передача может быть реализована как через callback-блоки (как в примерах выше), так и через RxSwift или Combine для реактивных потоков.
- Presenter никогда не знает о конкретных классах: Он работает только с абстракциями (протоколами), что обеспечивает тестируемость и гибкость.
Таким образом, презентер в VIPER получает данные преимущественно от Interactor в ответ на свои запросы или события от View, затем трансформирует их согласно бизнес-логике и передает в View для окончательного отображения. Этот четкий поток данных предотвращает смешение ответственности и делает код каждого компонента модульным и легко тестируемым.