viper - чистая архитектура ios-приложения (И. Чирков)
TRANSCRIPT
![Page 1: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/1.jpg)
VIPER - чистая архитектура iOS приложения
Проблемы, возникающие при использовании MVC, и их решение при помощи VIPER.
![Page 2: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/2.jpg)
ПланПроблемы MVC
Структура VIPER модуля
Сервисы
Data flow в модуле
Навигация
Data flow между модулями
Вложенные модули
Код
![Page 3: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/3.jpg)
Что такое MVC?
![Page 4: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/4.jpg)
Что такое MVC?
![Page 5: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/5.jpg)
MVC - Massive View Controller!
![Page 6: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/6.jpg)
MVC
![Page 7: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/7.jpg)
Чем должен занимается Controller?
Обновлять данные на View
Ловить события, генерируемые пользователем
![Page 8: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/8.jpg)
MVC в реальности
![Page 9: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/9.jpg)
Чем приходится занимается Controller-у
Обновлять данные на View
Ловить события, генерируемые пользователем
Являться делегатом разнообразных сервисов
Обрабатывать полученные данные
Отвечать за навигацию между экранами
Отвечать за поток данных между экранами
...
![Page 10: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/10.jpg)
Чем приходится занимается Controller-у
Обновлять данные на View
Ловить события, генерируемые пользователем
Являться делегатом разнообразных сервисов
Обрабатывать полученные данные
Отвечать за навигацию между экранами
Отвечать за поток данных между экранами
...
Massive View Controller
![Page 11: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/11.jpg)
Последствия Massive View Controller
Огромные классы
Нарушение принципов SOLID (куча ответственностей)
Сложно дебажить
Сложно вносить изменения
Сильная связность компонентов
Сложно/невозможно тестировать
...
![Page 12: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/12.jpg)
VIPERClean Architecture iOS приложения
![Page 13: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/13.jpg)
VIPER
View
Interactor
Presenter
Entity
Router
![Page 14: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/14.jpg)
Структура VIPER модуля
![Page 15: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/15.jpg)
Структура VIPER модуля
![Page 16: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/16.jpg)
View Presenter Interactor Router
override func viewDidLoad() { super.viewDidLoad() self.title = "Title"}
func setupTitle(title: String) { self.title = title}
override func viewDidLoad() { super.viewDidLoad() presenter.setup()} func setup() {
view.setupTitle("Title")}
![Page 17: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/17.jpg)
View Presenter Interactor Router
@IBAction func validateTouchUpInside() { let email = self.emailField.text if self.validateEmail(email) { self.presentSuccessScreen() } }@IBAction func validateTouchUpInside()
func validateButtonPressed(email: String)
func validateEmail(email: String) -> Bool
func presentSuccessScreen()
if () {}
![Page 18: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/18.jpg)
Сервисы
Разбиваем интерактор на сервисы
![Page 19: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/19.jpg)
Сервисы
![Page 20: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/20.jpg)
Data flow
![Page 21: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/21.jpg)
Data flow
![Page 22: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/22.jpg)
Навигация
SettingsModule GeneralSettingsModule
![Page 23: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/23.jpg)
SettingsModule GeneralSettingsModule
![Page 24: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/24.jpg)
protocol SettingsDisplayManagerDelegate: class { // DISPLAY MANAGER DELEGATE func didSelectCell()}
class SettingsDisplayManager: NSObject, UITableViewDelegate { // DISPLAY MANAGER weak var delegate: SettingsDisplayManagerDelegate? // VIEW func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { delegate?.didSelectCell() }}
class SettingsView : UIViewController, SettingsDisplayManagerDelegate { // VIEW var output: SettingsPresenter! var displayManager: SettingsDisplayManager! func didSelectCell() { output.didSelectCell() }}
![Page 25: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/25.jpg)
class SettingsPresenter { // PRESENTER var view: SettingsView! var router: SettingsRouter! func didSelectCell() { router.presentGeneralSetting() } }
class SettingsRouter { // ROUTER var view: SettingsView! func presentGeneralSetting() { let generalSettingsModule = GeneralSettingsModule() //Инициализируем модуль GeneralSettingModule let generalSettingsView = generalSettingsModule.view // Забираем у него View view.navigationController?.pushViewController(generalSettingsView, animated: true) }}
![Page 26: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/26.jpg)
DataFlow между модулями
![Page 27: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/27.jpg)
NewsModule DetailModule
NewsID
![Page 28: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/28.jpg)
NewsModule DetailModule
[NewsItem]
[NewsItem]
[NewsItem] [NewsItem]NewsID
NewsID
NewsID
NewsID
NewsIDNewsItem
NewsItem
Done
![Page 29: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/29.jpg)
NewsModule DetailModule
NewsID
NewsID
![Page 30: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/30.jpg)
NewsModule DetailModule
NewsID NewsID
outputHandler outputHandler
![Page 31: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/31.jpg)
class NewsPresenter: DetailModuleOutput { // PRESENTER func didSelectNews(newsId: Int) { router.presentDetails(self) } func newsAddedToFavorites(newsId: Int) { }}
class NewsRouter { // ROUTER var view: NewsView! func presentDetails(outputHandler: DetailModuleOutput) { let detailModule = DetailModule(outputHandler: DetailModuleOutput)//Инициализируем модуль let detailModuleView = detailModule.view // Забираем у него View view.navigationController?.pushViewController(detailModuleView, animated: true) }}
protocol DetailModuleOutput: class { // OutputHandler Protocol func newsAddedToFavorites(newsId: Int)}
![Page 32: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/32.jpg)
class DetailPresenter { // PRESENTER weak var outputHandler: DetailModuleOutput? func newsAddedToFavorite(newsId: Int) { // Метод вызван интерактором (DetailInteractor) outputHandler?.newsAddedToFavorites(newsId) }}
class NewsPresenter: DetailModuleOutput { // PRESENTER func didSelectNews(newsId: Int) { router.presentDetails(self) }
func newsAddedToFavorites(newsId: Int) { // DONE! }}
![Page 33: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/33.jpg)
Вложенные модули
![Page 34: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/34.jpg)
![Page 35: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/35.jpg)
Данные мастера
Портфолио
Услуги
Расписание
Отзывы
![Page 36: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/36.jpg)
Немного кода
![Page 37: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/37.jpg)
Инициализация модуля
let newsModule = NewsModule() //Инициализируем модуль NewsModule let newsView = newsModule.view // Забираем у него View view.navigationController?.pushViewController(newsView, animated: true)
![Page 38: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/38.jpg)
class NewsModule: NSObject { private var viewController: NewsViewController? var view: UIViewController { guard let view = viewController else { viewController = NewsViewController(nibName: "NewsViewController", bundle: nil) configureModule(viewController!) return viewController! } return view }
private func configureModule(view: NewsViewController) { // Устанавливает зависимости модуля. let presenter = NewsPresenter() let router = NewsRouter() let interactor = NewsInteractor() router.view = view view.output = presenter view.router = router presenter.view = view presenter.router = router presenter.interactor = interactor interactor.output = presenter }}
![Page 39: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/39.jpg)
Viewprotocol NewsViewInput: class { func updateView(news: [NewsItem])}
protocol NewsViewOutput: class { func setupView()}
class NewsViewController: UIViewController, NewsViewInput, NewsDisplayManagerDelegate { var output: NewsViewOutput! // Ссылка на Presenter let displayManager = NewsDisplayManager()
override func viewDidLoad() { super.viewDidLoad() output.setupView() // View сообщает Presenter-у о готовности }
func updateView(news: [NewsItem]) { // View получила от Presenter-а массив моделей новостей displayManager.updateTable(news) }}
![Page 40: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/40.jpg)
Presenterclass NewsPresenter: NewsViewOutput, NewsInteractorOutput { weak var view: NewsViewInput! var router: NewsRouter! var interactor: NewsInteractorInput!
func setupView() { interactor.obtainNews() // Presenter запрашивает список новостей у Interactor-a }
func newsObtained(cards: [CardItem]) { if news.count == 0 { view.showPlaceholder() } else { view.hidePlaceholder() view.updateView(news) // Presenter отправляет список полученных новостей View-шке } }
}
![Page 41: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/41.jpg)
Interactorprotocol PaymentInteractorInput: class { func obtainNews()}
protocol PaymentInteractorOutput: class { func newsObtained(news: [NewsItem]) func errorReceived(message: String)}
class NewsInteractor: NewsInteractorInput { weak var output: NewsInteractorOutput! let newsService = NewsService()
func obtainNews() { newsService.obtainNews( success: { news in self.output.newsObtained(news) }, failure: { error in self.output.errorReceived(error.localizedDescription) } ) }}
![Page 42: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/42.jpg)
Routerclass NewsRouter { weak var view: UIViewController!
func presentDetails(newsId: Int) { let detailModule = DetailModule() //Инициализируем модуль NewsModule let detailView = detailModule.view // Забираем у него View view.navigationController?.pushViewController(detailView, animated: true) }
func presentError(message: String) { let alert = UIAlertController(title: "ERROR_TITLE".localized, message: message, preferredStyle: .Alert) alert.addAction(UIAlertAction(title: "OK".localized, style: .Default, handler: nil)) view.presentViewController(alert, animated: true, completion: nil) }
}
![Page 43: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/43.jpg)
Файлы VIPER модуляViewInputProtocol
ViewOutputProtocol
View
Presenter
Router
InteractorInputProtocol
InteractorOutputProtocol
InteractorИтого: 8 файлов на один модуль! о_О
![Page 44: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/44.jpg)
Кодогенерация. Vipergen
![Page 45: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/45.jpg)
![Page 46: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/46.jpg)
![Page 47: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/47.jpg)
Vipergenhttps://github.com/nsleader/vipergen
![Page 48: Viper - чистая архитектура iOS-приложения (И. Чирков)](https://reader033.vdocuments.site/reader033/viewer/2022052606/58729f1d1a28ab07208b55d1/html5/thumbnails/48.jpg)
VIPERClean Architecture iOS приложения