Ответ
VIPER — это архитектурный паттерн, разделяющий ответственность на пять компонентов: View, Interactor, Presenter, Entity, Router. Он обеспечивает высокую тестируемость и разделение модулей.
Пример: Модуль входа в систему (Login)
-
Entity (Модель данных)
struct User { let email: String } // Entity — это простые структуры данных, без логики. -
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)
}
}
- 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 — это когда ты сначала нихуя не понимаешь и хочешь всё сломать, но потом, когда проект растёт как на дрожжах, ты понимаешь, что эта чёткая структура спасает тебя от ебанины и хаоса. Тестировать можно каждый кусок по отдельности, и если один сдох — остальные живы. Но готовься писать дохуя кода, блядь, за всё приходится платить.