VIPER
то, о чем все говорят, но никто
не рассказывает
Автор
Егор Толстой
Ведущий iOS разработчик
Rambler&Co
Twitter: @igrekde
www.github.com/igrekde
CompositionViewController
@interface
@interface
[self updateContactsView];
CompositionViewController
@interface
[self updateContactsView];
CompositionViewController
- (void)saveMessageToDraft {
// Crash
}
CompositionViewController
- (void)sendMessage {
// Crash
}
CompositionViewController
@end
- (void)updateAddressBook {
// Crash
// Crash
// Crash
}
CompositionViewController
- (void)updateAddressBook {
if (invalidContacts) {
return;
}
}
@end
CompositionViewController
@end
CompositionViewController
@interface
CompositionViewController
Хотим 100% crash-free!
Хотим быстро и качественно!
Хотим чистый код!
Хотим модульность!
• Структура VIPER модуля
• Связь модулей
• Возможности использования VIPER
VIEW PRESENTER INTERACTOR
ROUTER
E
E
VIEW PRESENTER INTERACTOR
ROUTER
E
E
VIEW PRESENTER INTERACTOR
ROUTER
E
E
VIEW PRESENTER INTERACTOR
ROUTER
E
E
VIEW PRESENTER INTERACTOR
ROUTER
E
E
VIEW PRESENTER INTERACTOR
ROUTER
E
E
VIEW PRESENTER INTERACTOR
ROUTER
E
E
VIEW PRESENTER INTERACTOR
ROUTER
E
E
TABLEVIEW
CELLFACTORY
View Presenter Interactor Router
- (void)viewDidLoad {
[super viewDidLoad];
}
[self setupBarWithTitle:@"#mbltdev"];
- (void)setupBarWithTitle:(NSString *)title {
self.navigationItem.title = title;
}
View Presenter Interactor Router
- (void)viewDidLoad {
[super viewDidLoad];
}
[self setupBarWithTitle:@"#mbltdev"];
- (void)setupBarWithTitle:(NSString *)title {
self.navigationItem.title = title;
}
View Presenter Interactor Router
Lifecycle
[self setupBarWithTitle:@"#mbltdev"];
View Presenter Interactor Router
Lifecycle
- (void)setupBarWithTitle:(NSString *)title {
self.navigationItem.title = title;
}
Navbar Setup
View Presenter Interactor Router
Lifecycle
Navbar Setup
[self setupBarWithTitle:@"#mbltdev"];
Handles events
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events
BOOL valid = [self.inputValidator
validatePhoneNumber:phoneNumber];
if (valid) {
} else {
}
[self processToNextScreen];
[self showErrorAlertView];
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events
if (valid) {
} else {
}
[self processToNextScreen];
[self showErrorAlertView];
BOOL valid = [self.inputValidator
validatePhoneNumber:phoneNumber];
Data validation
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events
if (valid) {
} else {
}
[self showErrorAlertView];
Data validation
[self processToNextScreen];
Module Routing
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events
if (valid) {
} else {
}
Data validation Module Routing
[self showErrorAlertView];Shows data
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if (valid) {
} else {
}
if-else
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else
[rootSavingContext performBlock:^{
Message *message =
[Message MR_findFirst];
}];
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else
[rootSavingContext performBlock:^{
Message *message =
[Message MR_findFirst];
}];
Data storage
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else Data storage
NSURLSessionDataTask *dataTask =
[self.session dataTaskWithRequest:request];
[dataTask resume];
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else Data storage
NSURLSessionDataTask *dataTask =
[self.session dataTaskWithRequest:request];
[dataTask resume];
Networking
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else Data storage
Networking
- (void)prepareForSegue:(UIStoryboardSegue *)segue
sender:(id)sender {
segue.destinationViewController.inputData =
@"inputData";
}
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else Data storage
Networking
- (void)prepareForSegue:(UIStoryboardSegue *)segue
sender:(id)sender {
segue.destinationViewController.inputData =
@"inputData";
}
Segues
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else Data storage
Networking
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [tableView dequeueReusableCell];
[cell setupWithObject:cellObject];
}
Segues
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else Data storage
Networking
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [tableView dequeueReusableCell];
[cell setupWithObject:cellObject];
}
Segues
Table DataSource
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else Data storage
Networking
- (void)panGestureRecognizerDidChangeState:(id)sender {
CGPoint panPoint = [sender translationInView:self.view];
self.view.frame = CGRectMake(panPoint.x,
self.view.frame.origin.y,
self.view.frame.size.width,
self.view.frame.size.height);
}
Segues
Table DataSource
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else Data storage
Networking
- (void)panGestureRecognizerDidChangeState:(id)sender {
CGPoint panPoint = [sender translationInView:self.view];
self.view.frame = CGRectMake(panPoint.x,
self.view.frame.origin.y,
self.view.frame.size.width,
self.view.frame.size.height);
}
Simple gestures
Segues
Table DataSource
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else Data storage
Networking
Simple gestures
if (panPoint.x > 100.0f) {
}
[self showSideMenu];
Segues
Table DataSource
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else Data storage
Networking
Simple gestures
if (panPoint.x > 100.0f) {
}
[self showSideMenu];Side Menu
Segues
Table DataSource
View Presenter Interactor Router
Lifecycle
Navbar Setup
Handles events Data validation Module Routing
Shows data
if-else Data storage
Networking
Segues
Table DataSource
Simple gestures
Side Menu
if (panPoint.x > 100.0f) {
}
Complex gestures
VIEW PRESENTER INTERACTOR
ROUTER
E
E
VIEW PRESENTER INTERACTOR
ROUTER ESERVICE
SERVICE
SERVICE
E
Роутер
должен
роутить
VIEW PRESENTER INTERACTOR
ROUTER ESERVICE
SERVICE
SERVICE
E
ASSEMBLY
Экран
=
Модуль
Экран
=
Модуль
UIViewController
UIView
UITabBarController
Daemon
VIEW PRESENTER INTERACTOR
ROUTER ESERVICE
SERVICE
SERVICE
E
ASSEMBLY
VIEW PRESENTER INTERACTOR
ROUTER ESERVICE
SERVICE
SERVICE
E
ASSEMBLY
PRESENTER
INTERACTOR
VIEW
ROUTER
VIEW
PRESENTER
INTERACTOR
ROUTER
Модуль 1 Модуль 2
PRESENTER
INTERACTOR
VIEW
ROUTER
VIEW
PRESENTER
INTERACTOR
ROUTER
@"Module2Segue"
<TransitionHandler> <ModuleInput>
Модуль 1 Модуль 2
@implementation Router
[[self.transitionHandler openModuleUsingSegue:SegueIdentifier]
        thenChainUsingBlock:^void(id<SomeModuleInput> moduleInput) {
                [moduleInput moduleConfigurationMethod];
        }];
@end
PRESENTER
INTERACTOR
VIEW
ROUTER
VIEW
PRESENTER
INTERACTOR
ROUTER
Список контактов Карточка контакта
contactId
contact
MODULEVIEW
MODULEPRESENTER
MODULEINTERACTOR
MODULEROUTER <SUBMODULE2INPUT>
<SUBMODULE1INPUT>
<SUBMODULE3INPUT>
~ 99% Code Coverage
VIEW
TEST
pod 'MyApp/Core', '1.2.2'
pod 'MyApp/Modules/CalendarModule', '1.5'
pod 'MyApp/Modules/SettingsModule', '1.0.1'
pod 'SharedModules/Authorization', '2.3.4'
• VIPER увеличивает тестируемость приложения,
• VIPER увеличивает модульность приложения,
• VIPER делает нашу жизнь немного прекраснее.
One more thing...
❤
VIPER
Open Source
• Рамблер.Конференции - приложение
• Generamba - генератор
• VIPER McFlurry - библиотека
• The Book of VIPER - сборник статей
https://github.com/rambler-ios
Дизайн: студия «Рамблер Инфографика»

MBLTDev15: Egor Tolstoy, Rambler&Co