viper architecture
TRANSCRIPT
Екатерина КоровкинаИнженер-разработчик iOS
наш взгляд на вопросАрхитектура VIPER
VIPER. Наш взгляд на вопрос
Поддержка проекта без архитектуры
VIPER. Наш взгляд на вопрос
• TDD• Code review• ???
Процесс разработки
VIPER. Наш взгляд на вопрос
MVCController
Model View
VIPER. Наш взгляд на вопрос
MVVM
ViewController ViewModel Model
VIPER. Наш взгляд на вопрос
Presenter Data Store(Entity)View Interactor
VIPERRouter
VIPER. Наш взгляд на вопрос
Что же такое «модуль»?
VIPER. Наш взгляд на вопрос
Что же такое «модуль»?
VIPER. Наш взгляд на вопрос
Что же такое «модуль»?
VIPER. Наш взгляд на вопрос
class, NSObject
UIViewController
class,NSObject
class,NSObject
NSManagedObjectNSObject,struct
View Presenter Interactor Data Store(Entity)
Router
VIPER
VIPER. Наш взгляд на вопрос
Конфигурация модуля
VIPER. Наш взгляд на вопрос
Запуск конфигурации модуля
class MoviesListModuleInitializer: NSObject {
override func awakeFromNib() {
let configurator = factory.moduleConfigurator() configurator.configureModule(self.viewController) }}
VIPER. Наш взгляд на вопрос
Конфигурацияclass MoviesListModuleConfigurator { func configureModule(viewController: ViewController) {
let presenter = Presenter() let service = ServiceDefault()
… viewController.presenter = presenter presenter.builder = builder
… }}
VIPER. Наш взгляд на вопрос
let controller = UIViewController(data: data)let controller = UIViewController()controller.data = data
Проблема: Не хотим отказываться от Segue
Передача данных между модулями
VIPER. Наш взгляд на вопрос
Есть же метод
prepareForSegue
VIPER. Наш взгляд на вопрос
class BaseViewController: UIViewController, ModuleDataTransferProtocol {
func performSegueWithIdentifier(identifier: String, configurationBlock: (destination: ConfigurableController) -> Void) {
… } func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
var destination = segue.destinationViewController block(destination: destination) }}
Базовый контроллер
VIPER. Наш взгляд на вопрос
Открытие модуля func openMovieWithId(movieId: String) {
controller.performSegueWith(block: { destinationController in
destinationController.configureModule(movieId) })
}
VIPER. Наш взгляд на вопрос
Схема передачи данныхrouter.openMoviewWithId(id)
vC.performSegueWithBlock(…)
prepareForSegue(…)
configurationBlock(destination)
VIPER. Наш взгляд на вопрос
Дополнительные соглашения
• NSManagedObject не дальше интерактора• Service Oriented Architecture (SOA) за интерактором• Общие части в pod’ы
VIPER. Наш взгляд на вопрос
Пример экранаVIPER + Swift
VIPER. Наш взгляд на вопрос
Пример модуля
VIPER. Наш взгляд на вопрос
Viewprotocol MoviesListViewInput: class {
func showData(dataStructure: DataStructure)
func showError(error: NSError)
func showEmptyView()}
VIPER. Наш взгляд на вопрос
Presenter// Данные от вьюшкиprotocol MoviesListViewOutput {
func viewIsReadyToAppear()
func didSelectMovieWithIndex(index: Int)}
// Данные от интерактораprotocol MoviesListInteractorOutput: class {
func didObtainMoviesList(movies: [Movie]?)
func didFailToObtainMoviesListWithError(error: NSError)}
VIPER. Наш взгляд на вопрос
Interactor
//Получение данных для модуляprotocol MoviesListInteractorInput {
func obtainMoviesList()
}
VIPER. Наш взгляд на вопрос
Router// Открытие другого экранаprotocol MoviesListRouterInput {
func openMovieWithId(movieId: String)}
VIPER. Наш взгляд на вопрос
Конкретнее
View
Presenter
Interactor
Service
viewIsReadyToAppear()
obtainData()
fetchData()
VIPER. Наш взгляд на вопрос
Все идет хорошоService
Interactor
Presenter
completionBlock()Array <Objects>
didObtainData()Array <Objects>
VIPER. Наш взгляд на вопрос
Отображаем данные
Presenter
View
showData()
VIPER. Наш взгляд на вопрос
Все идет плохо
Service
Interactor
Presenter
View
completionBlock()Error
didFailWithError()
showError()
VIPER. Наш взгляд на вопрос
Пример экранаVIPER + Swift
VIPER. Наш взгляд на вопрос
Viewprotocol WriteUsViewInput: class {
func showEmailError()
func clearInputViews()
func showSpinner()
func hideSpinner() }
VIPER. Наш взгляд на вопрос
Presenterprotocol WriteUsOutput: class {
func userRequestToWriteUs(withName name: String, email: String, message: String)
func userWantsToGoBack(areFieldsEmpty: Bool)}
protocol WriteUsInteractorOutput: class { func didSendFeedbackMessage() func didFailToSendFeedbackMessage(error: NSError)}
VIPER. Наш взгляд на вопрос
Interactorprotocol WriteUsInteractorInput {
func isValidEmail(email: String) -> Bool
func sendMessage(fromAuthor username: String, email: String, message: String)
}
VIPER. Наш взгляд на вопрос
Routerprotocol WriteUsRouterInput: class {
func goBackFromViewController(viewController: UIViewController)
func showAlertExitSubmitAlert(actionHandler: AlertActionsHandler)
func showAlertFeedbackSendDidSucceed()
func showAlertFeedbackSendDidFail()}
VIPER. Наш взгляд на вопрос
Конкретнее
View
Presenter
Interactor
userRequestToWriteUs()
isValidEmail()
VIPER. Наш взгляд на вопрос
Все идет хорошоPresenter
Interactor
Service
sendMessage()
sendMessage()
VIPER. Наш взгляд на вопрос
Отображаем данные
Presenter
Router
showFeedbackSucceed()
VIPER. Наш взгляд на вопрос
Ввод не валиден Presenter
View
showEmailError()
VIPER. Наш взгляд на вопрос
Что получается
Presenter ServiceView Interactor
Router Local storage
Networkclient
VIPER. Наш взгляд на вопрос
Достоинства• Быстрый вход в новый проект• Приемлемый код даже у новичков• Легкое переиспользование созданных компонент• Легкая модификация/поддержка кода
VIPER. Наш взгляд на вопрос
Недостатки• Большое количество времени• Большое количество кода• Большое количество интеграционных тестов• Холивары на тему «Где должен быть такой-то код?»
VIPER. Наш взгляд на вопрос
А нужен ли VIPER?/// Контролер для отображения информации о
приложенииclass AboutViewController: UIViewController {
//Текст текущей версии приложения @IBOutlet weak var versionLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() let versionNumber = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") let buildNumber = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleVersion") versionLabel.text = "Версия \(versionNumber!) (\(buildNumber!))" }
/** Метод для открытия нашей странички в iTunes */ @IBAction func openAppstore(sender: AnyObject) { UIApplication.sharedApplication().openURL(NSURL(string: "https://itunes.apple.com/ru/app/lenta.ru-vse-novosti-dna-politika/id975805914?mt=8")!) }}
VIPER. Наш взгляд на вопрос
Выводы
• Длинные по времени проекты
• Необходимость подключения новых людей в середине проекта
Стоит использовать Не стоит использовать
• Рекламное приложение/визитка/прототипирование
• Стартап, когда нужно выпустить MVP как можно быстрее
VIPER. Наш взгляд на вопрос
Open Source• Рамблер.Конференции — приложение• Generamba — генератор• VIPER McFlurry — библиотека• The Book of VIPER — сборник статей
https://github.com/rambler-ios
VIPER. Наш взгляд на вопрос
THANKS!