Опиши пример реализации архитектуры VIPER на iOS.

Ответ

VIPER — это архитектурный паттерн, разделяющий ответственность на пять компонентов: View, Interactor, Presenter, Entity, Router. Он обеспечивает высокую тестируемость и разделение модулей.

Пример: Модуль входа в систему (Login)

  1. Entity (Модель данных)

    struct User {
    let email: String
    }
    // Entity — это простые структуры данных, без логики.
  2. Interactor (Бизнес-логика)

    
    protocol LoginInteractorInputProtocol: AnyObject {
    func loginUser(email: String, password: String)
    }

protocol LoginInteractorOutputProtocol: AnyObject { func loginDidSucceed(user: User) func loginDidFail(error: Error) }

final class LoginInteractor: LoginInteractorInputProtocol { weak var presenter: LoginInteractorOutputProtocol? private let authService: AuthServiceProtocol // Зависимость для работы с сетью/БД

func loginUser(email: String, password: String) {
    authService.login(email: email, password: password) { [weak self] result in
        switch result {
        case .success(let user):
            self?.presenter?.loginDidSucceed(user: user)
        case .failure(let error):
            self?.presenter?.loginDidFail(error: error)
        }
    }
}

}


3.  **Presenter (Логика представления)**
```swift
protocol LoginPresenterProtocol: AnyObject {
    func viewDidLoad()
    func didTapLoginButton(email: String?, password: String?)
}

final class LoginPresenter: LoginPresenterProtocol {
    weak var view: LoginViewProtocol?
    var interactor: LoginInteractorInputProtocol?
    var router: LoginRouterProtocol?

    func didTapLoginButton(email: String?, password: String?) {
        guard let email = email, let password = password, !email.isEmpty, !password.isEmpty else {
            view?.showValidationError()
            return
        }
        view?.showLoadingIndicator(true)
        interactor?.loginUser(email: email, password: password)
    }
}

// Presenter также реализует протокол вывода Interactor'а
extension LoginPresenter: LoginInteractorOutputProtocol {
    func loginDidSucceed(user: User) {
        view?.showLoadingIndicator(false)
        router?.routeToHomeScreen(with: user)
    }

    func loginDidFail(error: Error) {
        view?.showLoadingIndicator(false)
        view?.showError(message: error.localizedDescription)
    }
}
  1. View (Отображение)
    
    protocol LoginViewProtocol: AnyObject {
    func showLoadingIndicator(_ show: Bool)
    func showError(message: String)
    func showValidationError()
    }

final class LoginViewController: UIViewController, LoginViewProtocol { var presenter: LoginPresenterProtocol! @IBOutlet private weak var emailTextField: UITextField!

override func viewDidLoad() {
    super.viewDidLoad()
    presenter.viewDidLoad()
}

@IBAction private func loginButtonTapped() {
    presenter.didTapLoginButton(email: emailTextField.text, password: "...")
}

// MARK: - LoginViewProtocol
func showError(message: String) {
    let alert = UIAlertController(title: "Ошибка", message: message, preferredStyle: .alert)
    present(alert, animated: true)
}
// ... остальные методы протокола

}


5.  **Router (Навигация)**
```swift
protocol LoginRouterProtocol: AnyObject {
    func routeToHomeScreen(with user: User)
}

final class LoginRouter: LoginRouterProtocol {
    weak var viewController: UIViewController?

    func routeToHomeScreen(with user: User) {
        let homeVC = HomeModuleBuilder.build(with: user)
        viewController?.navigationController?.pushViewController(homeVC, animated: true)
    }
}

Сборка модуля (Assembly/Builder):

enum LoginModuleBuilder {
    static func build() -> UIViewController {
        let view = LoginViewController()
        let presenter = LoginPresenter()
        let interactor = LoginInteractor()
        let router = LoginRouter()

        view.presenter = presenter
        presenter.view = view
        presenter.interactor = interactor
        presenter.router = router
        interactor.presenter = presenter
        router.viewController = view

        return view
    }
}

Преимущества VIPER: Четкое разделение ответственности, высокая тестируемость каждого компонента изолированно, удобство поддержки больших проектов.

Ответ 18+ 🔞

Э, слушай, смотри, вот этот VIPER, блядь. Ну, архитектурный паттерн, говорит. А звучит-то как, ёпта, — змея ядовитая. Так и есть, сука: пока разберёшься, кто за что отвечает, уже от укуса помрёшь, в рот меня чих-пых!

Ну, короче, делит он всю эту движуху на пять слоёв, блядь. View, Interactor, Presenter, Entity, Router. Каждый, сука, в своей норе сидит и свою хуйню делает. В итоге тестируемость — овердохуища, и модули друг про друга нихуя не знают. Красота, внатуре.

Смотри, как это на примере входа в приложение выглядит, блядь.

1. Entity (Модель данных) Это просто структурики, блядь, голые данные. Никакой логики, нихуя. Как мешки с картошкой, только таскать их.

struct User {
    let email: String
}

2. Interactor (Бизнес-логика) А вот это уже работяга, блядь! Он, сука, всю грязную работу делает: в сеть лезет, в базу данных хуярит, логику проверяет. Presenter ему говорит: «Войди этого пользователя!», а Interactor такой: «Ща, бля, организую».

final class LoginInteractor: LoginInteractorInputProtocol {
    weak var presenter: LoginInteractorOutputProtocol?
    private let authService: AuthServiceProtocol // Вот эта хуйня с сетью общается

    func loginUser(email: String, password: String) {
        authService.login(email: email, password: password) { [weak self] result in
            switch result {
            case .success(let user):
                self?.presenter?.loginDidSucceed(user: user) // Всё, залогинил, сука!
            case .failure(let error):
                self?.presenter?.loginDidFail(error: error) // Обосрался, блядь
            }
        }
    }
}

3. Presenter (Логика представления) Это, блядь, самый главный понторез и организатор. View к нему пристаёт: «Юзер ткнул кнопку!», а он данные проверит, Interactor'у команду даст, а потом результат обратно во View выведет. Никакого UIKit'а не знает, чистая логика, блядь. Тестировать — одно удовольствие.

final class LoginPresenter: LoginPresenterProtocol {
    weak var view: LoginViewProtocol?
    var interactor: LoginInteractorInputProtocol?
    var router: LoginRouterProtocol?

    func didTapLoginButton(email: String?, password: String?) {
        // Проверяем, не пустые ли поля, а то юзеры — распиздяи конченые
        guard let email = email, let password = password, !email.isEmpty, !password.isEmpty else {
            view?.showValidationError()
            return
        }
        view?.showLoadingIndicator(true)
        interactor?.loginUser(email: email, password: password) // Пошёл, Interactor, работай!
    }
}

extension LoginPresenter: LoginInteractorOutputProtocol {
    func loginDidSucceed(user: User) {
        view?.showLoadingIndicator(false)
        router?.routeToHomeScreen(with: user) // Всё, гоу на следующий экран!
    }

    func loginDidFail(error: Error) {
        view?.showLoadingIndicator(false)
        view?.showError(message: error.localizedDescription) // Выводим, блядь, ошибку
    }
}

4. View (Отображение) Это тупая рожа приложения, блядь. Кнопки, текстфилды, спиннеры. Её задача — показывать то, что Presenter скажет, и тыкать в него, когда юзер что-то сделал. Ума палаты не надо.

final class LoginViewController: UIViewController, LoginViewProtocol {
    var presenter: LoginPresenterProtocol! // Вот его начальник
    @IBOutlet private weak var emailTextField: UITextField!

    @IBAction private func loginButtonTapped() {
        // Юзер ткнул — пошёл докладывать начальству
        presenter.didTapLoginButton(email: emailTextField.text, password: "...")
    }

    // MARK: - LoginViewProtocol
    func showError(message: String) {
        // Просто показываем алерт, как Presenter приказал
        let alert = UIAlertController(title: "Ошибка", message: message, preferredStyle: .alert)
        present(alert, animated: true)
    }
    // ... остальные методы протокола
}

5. Router (Навигация) Это, блядь, швейцар или лифтёр. Его задача — переводить с этажа на этаж. Presenter говорит: «Валим отсюда на домашний экран!», а Router уже знает, какую дверь открыть.

final class LoginRouter: LoginRouterProtocol {
    weak var viewController: UIViewController?

    func routeToHomeScreen(with user: User) {
        let homeVC = HomeModuleBuilder.build(with: user) // Собираем следующий модуль
        viewController?.navigationController?.pushViewController(homeVC, animated: true) // Пиздуем!
    }
}

И главное, блядь — сборка! (Assembly/Builder) А то эти все компоненты друг друга не найдут, разбегутся, сука. Нужен специальный монтажник, который их всех скрутит в кучу.

enum LoginModuleBuilder {
    static func build() -> UIViewController {
        let view = LoginViewController()
        let presenter = LoginPresenter()
        let interactor = LoginInteractor()
        let router = LoginRouter()

        // А теперь, блядь, начинается самое весёлое — подключение проводов
        view.presenter = presenter
        presenter.view = view
        presenter.interactor = interactor
        presenter.router = router
        interactor.presenter = presenter
        router.viewController = view

        return view // Всё, модуль готов, можно пользоваться
    }
}

Итог, ёпта: VIPER — это когда ты сначала нихуя не понимаешь и хочешь всё сломать, но потом, когда проект растёт как на дрожжах, ты понимаешь, что эта чёткая структура спасает тебя от ебанины и хаоса. Тестировать можно каждый кусок по отдельности, и если один сдох — остальные живы. Но готовься писать дохуя кода, блядь, за всё приходится платить.