Как презентер в архитектуре VIPER/MVP получает данные?

Ответ

Презентер получает готовые доменные модели от Интерактора (в VIPER) или Репоизтория/Сервиса (в MVP). Передача данных происходит асинхронно через:

  1. Замыкания (Closures) — наиболее распространенный способ.
  2. Протоколы/Делегаты — для более строгой связи.
  3. Реактивные потоки (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 попроще — то сразу Репозиторий или какой-нибудь Сервис. Главное — не самому лезть, а поручить, понимаешь?

А передают они ему это добро асинхронно, потому что ждать — это зашквар. И есть три основных способа, как эту посылку вручить:

  1. Замыкания (Closures) — самый народный, простой способ. Типа «на, держи, когда будет готово, позвони в этот коллбэк».
  2. Протоколы/Делегаты — для любителей строгой отчетности и бумажек. Более жесткая связь, всё по контракту.
  3. Реактивные потоки (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)
                }
            }
        }
    }
}

Видишь? Презентер говорит сервису: «Эй, принеси-ка мне профиль пользователя, вот тебе ящик (замыкание), положи туда, когда будет». Сервис сходит, побъётся с сетью или базой, и кладёт в ящик либо профиль, либо ошибку. А презентер, получив ящик, уже решает, что показать вьюхе: данные или сообщение «ой, всё».

И вот тут, блядь, ключевой принцип, который многие просрать пытаются: Презентер НИКОГДА, В РОТ ЕБИСЬ, НИКОГДА не лезет сам в сетевой слой или базу данных! Он общается только через абстракцию — через протокол. Это, во-первых, делает его тестируемым (можно подсунуть муляж сервиса), а во-вторых, соблюдает тот самый Принцип единственной ответственности. Он не должен знать, откуда ноги растут у данных, его дело — получить и отдать на отображение. Всё, пицца приехала, можешь расходиться.