Ответ
Презентер получает готовые доменные модели от Интерактора (в VIPER) или Репоизтория/Сервиса (в MVP). Передача данных происходит асинхронно через:
- Замыкания (Closures) — наиболее распространенный способ.
- Протоколы/Делегаты — для более строгой связи.
- Реактивные потоки (Combine/RxSwift) — для потоковой передачи данных.
Пример с замыканием:
protocol DataServiceProtocol {
func fetchUserProfile(completion: @escaping (Result<UserProfile, Error>) -> Void)
}
class ProfilePresenter {
private let service: DataServiceProtocol
weak var view: ProfileViewProtocol?
init(service: DataServiceProtocol) {
self.service = service
}
func viewDidLoad() {
service.fetchUserProfile { [weak self] result in
// Обработка результата в главном потоке
DispatchQueue.main.async {
switch result {
case .success(let profile):
self?.view?.displayProfile(profile)
case .failure(let error):
self?.view?.displayError(error)
}
}
}
}
}
Ключевой принцип: Презентер никогда не обращается напрямую к сетевому слою или базе данных. Он запрашивает данные у абстракции (протокола), что обеспечивает тестируемость и соблюдение Single Responsibility Principle.
Ответ 18+ 🔞
А, слушай, смотри, как это работает, блядь. Вот представь: Презентер — это такой важный начальник, который сам ничего не делает, но всем заправляет. И ему, сука, нужны данные. Но он же не пойдёт сам в базу данных ковыряться или по апишкам ползать, это ниже его достоинства, ёпта!
Ему эти данные, готовенькие, доменные модели, приносят на блюдечке. Кто приносит? Ну, смотри: если у тебя архитектура VIPER — то это Интерактор, такой работяга-посредник. А если MVP попроще — то сразу Репозиторий или какой-нибудь Сервис. Главное — не самому лезть, а поручить, понимаешь?
А передают они ему это добро асинхронно, потому что ждать — это зашквар. И есть три основных способа, как эту посылку вручить:
- Замыкания (Closures) — самый народный, простой способ. Типа «на, держи, когда будет готово, позвони в этот коллбэк».
- Протоколы/Делегаты — для любителей строгой отчетности и бумажек. Более жесткая связь, всё по контракту.
- Реактивные потоки (Combine/RxSwift) — это уже для крутых ребят, которые любят, чтобы данные сами текли рекой. Подписался — и получай.
Вот, смотри, как это выглядит на практике с замыканием, самый частый случай:
protocol DataServiceProtocol {
func fetchUserProfile(completion: @escaping (Result<UserProfile, Error>) -> Void)
}
class ProfilePresenter {
private let service: DataServiceProtocol
weak var view: ProfileViewProtocol?
init(service: DataServiceProtocol) {
self.service = service
}
func viewDidLoad() {
service.fetchUserProfile { [weak self] result in
// Обработка результата в главном потоке
DispatchQueue.main.async {
switch result {
case .success(let profile):
self?.view?.displayProfile(profile)
case .failure(let error):
self?.view?.displayError(error)
}
}
}
}
}
Видишь? Презентер говорит сервису: «Эй, принеси-ка мне профиль пользователя, вот тебе ящик (замыкание), положи туда, когда будет». Сервис сходит, побъётся с сетью или базой, и кладёт в ящик либо профиль, либо ошибку. А презентер, получив ящик, уже решает, что показать вьюхе: данные или сообщение «ой, всё».
И вот тут, блядь, ключевой принцип, который многие просрать пытаются: Презентер НИКОГДА, В РОТ ЕБИСЬ, НИКОГДА не лезет сам в сетевой слой или базу данных! Он общается только через абстракцию — через протокол. Это, во-первых, делает его тестируемым (можно подсунуть муляж сервиса), а во-вторых, соблюдает тот самый Принцип единственной ответственности. Он не должен знать, откуда ноги растут у данных, его дело — получить и отдать на отображение. Всё, пицца приехала, можешь расходиться.