48
VIPER - чистая архитектура iOS приложения Проблемы, возникающие при использовании MVC, и их решение при помощи VIPER.

Viper - чистая архитектура iOS-приложения (И. Чирков)

  • Upload
    65apps

  • View
    975

  • Download
    7

Embed Size (px)

Citation preview

Page 1: Viper - чистая архитектура iOS-приложения (И. Чирков)

VIPER - чистая архитектура iOS приложения

Проблемы, возникающие при использовании MVC, и их решение при помощи VIPER.

Page 2: Viper - чистая архитектура iOS-приложения (И. Чирков)

ПланПроблемы MVC

Структура VIPER модуля

Сервисы

Data flow в модуле

Навигация

Data flow между модулями

Вложенные модули

Код

Page 3: Viper - чистая архитектура iOS-приложения (И. Чирков)

Что такое MVC?

Page 4: Viper - чистая архитектура iOS-приложения (И. Чирков)

Что такое MVC?

Page 5: Viper - чистая архитектура iOS-приложения (И. Чирков)

MVC - Massive View Controller!

Page 6: Viper - чистая архитектура iOS-приложения (И. Чирков)

MVC

Page 7: Viper - чистая архитектура iOS-приложения (И. Чирков)

Чем должен занимается Controller?

Обновлять данные на View

Ловить события, генерируемые пользователем

Page 8: Viper - чистая архитектура iOS-приложения (И. Чирков)

MVC в реальности

Page 9: Viper - чистая архитектура iOS-приложения (И. Чирков)

Чем приходится занимается Controller-у

Обновлять данные на View

Ловить события, генерируемые пользователем

Являться делегатом разнообразных сервисов

Обрабатывать полученные данные

Отвечать за навигацию между экранами

Отвечать за поток данных между экранами

...

Page 10: Viper - чистая архитектура iOS-приложения (И. Чирков)

Чем приходится занимается Controller-у

Обновлять данные на View

Ловить события, генерируемые пользователем

Являться делегатом разнообразных сервисов

Обрабатывать полученные данные

Отвечать за навигацию между экранами

Отвечать за поток данных между экранами

...

Massive View Controller

Page 11: Viper - чистая архитектура iOS-приложения (И. Чирков)

Последствия Massive View Controller

Огромные классы

Нарушение принципов SOLID (куча ответственностей)

Сложно дебажить

Сложно вносить изменения

Сильная связность компонентов

Сложно/невозможно тестировать

...

Page 12: Viper - чистая архитектура iOS-приложения (И. Чирков)

VIPERClean Architecture iOS приложения

Page 13: Viper - чистая архитектура iOS-приложения (И. Чирков)

VIPER

View

Interactor

Presenter

Entity

Router

Page 14: Viper - чистая архитектура iOS-приложения (И. Чирков)

Структура VIPER модуля

Page 15: Viper - чистая архитектура iOS-приложения (И. Чирков)

Структура VIPER модуля

Page 16: Viper - чистая архитектура iOS-приложения (И. Чирков)

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-приложения (И. Чирков)

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-приложения (И. Чирков)

Сервисы

Разбиваем интерактор на сервисы

Page 19: Viper - чистая архитектура iOS-приложения (И. Чирков)

Сервисы

Page 20: Viper - чистая архитектура iOS-приложения (И. Чирков)

Data flow

Page 21: Viper - чистая архитектура iOS-приложения (И. Чирков)

Data flow

Page 22: Viper - чистая архитектура iOS-приложения (И. Чирков)

Навигация

SettingsModule GeneralSettingsModule

Page 23: Viper - чистая архитектура iOS-приложения (И. Чирков)

SettingsModule GeneralSettingsModule

Page 24: Viper - чистая архитектура iOS-приложения (И. Чирков)

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-приложения (И. Чирков)

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-приложения (И. Чирков)

DataFlow между модулями

Page 27: Viper - чистая архитектура iOS-приложения (И. Чирков)

NewsModule DetailModule

NewsID

Page 28: Viper - чистая архитектура iOS-приложения (И. Чирков)

NewsModule DetailModule

[NewsItem]

[NewsItem]

[NewsItem] [NewsItem]NewsID

NewsID

NewsID

NewsID

NewsIDNewsItem

NewsItem

Done

Page 29: Viper - чистая архитектура iOS-приложения (И. Чирков)

NewsModule DetailModule

NewsID

NewsID

Page 30: Viper - чистая архитектура iOS-приложения (И. Чирков)

NewsModule DetailModule

NewsID NewsID

outputHandler outputHandler

Page 31: Viper - чистая архитектура iOS-приложения (И. Чирков)

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-приложения (И. Чирков)

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-приложения (И. Чирков)

Вложенные модули

Page 34: Viper - чистая архитектура iOS-приложения (И. Чирков)
Page 35: Viper - чистая архитектура iOS-приложения (И. Чирков)

Данные мастера

Портфолио

Услуги

Расписание

Отзывы

Page 36: Viper - чистая архитектура iOS-приложения (И. Чирков)

Немного кода

Page 37: Viper - чистая архитектура iOS-приложения (И. Чирков)

Инициализация модуля

let newsModule = NewsModule() //Инициализируем модуль NewsModule let newsView = newsModule.view // Забираем у него View view.navigationController?.pushViewController(newsView, animated: true)

Page 38: Viper - чистая архитектура iOS-приложения (И. Чирков)

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-приложения (И. Чирков)

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-приложения (И. Чирков)

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-приложения (И. Чирков)

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-приложения (И. Чирков)

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-приложения (И. Чирков)

Файлы VIPER модуляViewInputProtocol

ViewOutputProtocol

View

Presenter

Router

InteractorInputProtocol

InteractorOutputProtocol

InteractorИтого: 8 файлов на один модуль! о_О

Page 44: Viper - чистая архитектура iOS-приложения (И. Чирков)

Кодогенерация. Vipergen

Page 45: Viper - чистая архитектура iOS-приложения (И. Чирков)
Page 46: Viper - чистая архитектура iOS-приложения (И. Чирков)
Page 47: Viper - чистая архитектура iOS-приложения (И. Чирков)

Vipergenhttps://github.com/nsleader/vipergen

Page 48: Viper - чистая архитектура iOS-приложения (И. Чирков)

VIPERClean Architecture iOS приложения