Разбираем massive view controller
TRANSCRIPT
Александр Сычев Разработчик iOS
RAMBLER.iOS V
Как разобрать Massive View Controller и сделать из него VIPER-модуль
Massive View Controller -> VIPER
Проблемы Massive View Controller
Примеры
Немного статистики
Massive View Controller -> VIPER
Проблемы Massive View Controller
Примеры
Немного статистики
Massive View Controller -> VIPER
Dark Massive View Controller Light VIPER
Massive View Controller -> VIPER
Model View Controller
ModelView
Controller
Mediator Strategy
User action
Update
Update
Notify
Massive View Controller -> VIPER
Massive View Controller
Massive View Controller -> VIPER
•Высокая сложность поддержки и развития
•Высокий порог вхождения
•Слабо тестируем
•Невозможно переиспользовать
•Редактор притормаживает на 5 000 строк кода 😃
Недостатки Massive View Controller
Massive View Controller -> VIPER
Massive View Controller -> VIPER
Проблемы Massive View Controller
Примеры
Немного статистики
Massive View Controller -> VIPER
Massive View Controller -> VIPER
Massive View Controller -> VIPER
Переход между экранами
Massive View Controller -> VIPER
View Presenter Interactor Router
Переход между экранами
Massive View Controller -> VIPER
@protocol AGMainMenuControllerViewOutput <NSObject>
- (void)showMenuSectionWithType:(MainMenuSectionType)sectionType fromViewController:(UIViewController *)viewController;
@end
View Presenter Interactor Router
Massive View Controller -> VIPER
View Presenter Interactor Router
Handle event
@protocol AGMainMenuControllerViewOutput <NSObject>
- (void)showMenuSectionWithType:(MainMenuSectionType)sectionType fromViewController:(UIViewController *)viewController;
@end
Massive View Controller -> VIPER
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSInteger index = indexPath.row; [self.output showMenuSectionWithType:index fromViewController:self];}
View Presenter Interactor Router
Handle event
Massive View Controller -> VIPER
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSInteger index = indexPath.row; [self.output showMenuSectionWithType:index fromViewController:self];}
View Presenter Interactor Router
Handle eventTable delegate
Massive View Controller -> VIPER
@protocol AGMainMenuRouterInput <NSObject>
- (void)showCityFromViewController:(UIViewController *)vcS;- (void)showPlacesFromViewController:(UIViewController *)vcS;- (void)showReferenceBookFromViewController:(UIViewController *)vcS;
@end
View Presenter Interactor Router
Handle eventTable delegate
Massive View Controller -> VIPER
@protocol AGMainMenuRouterInput <NSObject>
- (void)showCityFromViewController:(UIViewController *)vcS;- (void)showPlacesFromViewController:(UIViewController *)vcS;- (void)showReferenceBookFromViewController:(UIViewController *)vcS;
@end
View Presenter Interactor Router
Handle eventTable delegate Module routing
Massive View Controller -> VIPER
- (void)showMenuSectionWithType:(MainMenuSectionType)sectionType fromViewController:(UIViewController *)viewController { switch (sectionType) { <Call RouterInput methods> }}
View Presenter Interactor Router
Handle eventTable delegate Module routing
Massive View Controller -> VIPER
- (void)showMenuSectionWithType:(MainMenuSectionType)sectionType fromViewController:(UIViewController *)viewController { switch (sectionType) { <Call RouterInput methods> }}
View Presenter Interactor Router
Handle eventTable delegate Module routing
Call Router
Massive View Controller -> VIPER
- (void)showCityFromViewController:(UIViewController *)vcS { UIStoryboard *sb = <Load storyboard>; UIViewController *vcD = <Instantiate ViewController>; [vcS.navigationController pushViewController:vcD animated:YES];}
View Presenter Interactor Router
Handle eventTable delegate Module routing
Call Router
Massive View Controller -> VIPER
Переход между экранами
Massive View Controller VIPER
Massive View Controller -> VIPER
Чтение данных
Massive View Controller -> VIPER
Чтение данных
View Presenter Interactor Router
Massive View Controller -> VIPER
@protocol AGMainMenuControllerViewOutput <NSObject>
- (void)obtainQuarters;
@end
View Presenter Interactor Router
Massive View Controller -> VIPER
View Presenter Interactor Router
@protocol AGMainMenuControllerViewOutput <NSObject>
- (void)obtainQuarters;
@end
Handle event
Massive View Controller -> VIPER
- (void)viewDidLoad { [super viewDidLoad]; [self.output obtainQuarters];}
View Presenter Interactor Router
Handle event
Massive View Controller -> VIPER
- (void)viewDidLoad { [super viewDidLoad]; [self.output obtainQuarters];}
View Presenter Interactor Router
Handle eventAsk for data
Massive View Controller -> VIPER
- (void)obtainQuarters { [self.view showSpinners]; [self.interactor loadQuarters];}
View Presenter Interactor Router
Handle eventAsk for data
Massive View Controller -> VIPER
- (void)obtainQuarters { [self.view showSpinners]; [self.interactor loadQuarters];}
View Presenter Interactor Router
Handle eventAsk for data
Call Interactor
Massive View Controller -> VIPER
- (void)loadQuarters { <Prepare loading request> NSArray *quarters = [AGQuarter MR_findAllSortedBy:sortTerm inContext:context]; NSArray *ponsoQuarters = [self createPlainObjectsFrom:quarters]; [self.output loadedQuarters:ponsoQuarters];}
View Presenter Interactor Router
Handle eventAsk for data
Call Interactor
Massive View Controller -> VIPER
- (void)loadQuarters { <Prepare loading request> NSArray *quarters = [AGQuarter MR_findAllSortedBy:sortTerm inContext:context]; NSArray *ponsoQuarters = [self createPlainObjectsFrom:quarters]; [self.output loadedQuarters:ponsoQuarters];}
View Presenter Interactor Router
Handle eventAsk for data
Call Interactor
Load data
Massive View Controller -> VIPER
- (void)loadedQuarters:(NSArray *)quarters { [self.view hideSpinners]; [self.view handleObtainedQuarters:quarters];}
View Presenter Interactor Router
Handle eventAsk for data
Call Interactor
Load data
Massive View Controller -> VIPER
- (void)loadedQuarters:(NSArray *)quarters { [self.view hideSpinners]; [self.view handleObtainedQuarters:quarters];}
View Presenter Interactor Router
Handle eventAsk for data
Call Interactor
Load data
Prepare data
Massive View Controller -> VIPER
View Presenter Interactor Router
Handle eventAsk for data
Call Interactor
Load data
Prepare data
- (void)handleObtainedQuarters:(NSArray *)quarters { <Display quarters>}
Massive View Controller -> VIPER
- (void)handleObtainedQuarters:(NSArray *)quarters { <Display quarters>}
View Presenter Interactor Router
Handle eventAsk for data
Call Interactor
Load data
Prepare data
Display data
Massive View Controller -> VIPER
Чтение данных
Massive View Controller VIPER
Massive View Controller -> VIPER
Конфигурация объектов
Massive View Controller -> VIPER
AssemblyVIPER-модуль
Конфигурация объектов
Massive View Controller -> VIPER
@interface AGMainMenuInteractor : NSObject <AGMainMenuInteractorInput>
@property (weak) id<AGMainMenuInteractorOutput> output;
@property (strong) id<AGImageLocalCacheManager> imageLocalCacheManager;
@end
AssemblyVIPER-модуль
Massive View Controller -> VIPER
@interface AGMainMenuInteractor : NSObject <AGMainMenuInteractorInput>
@property (weak) id<AGMainMenuInteractorOutput> output;
@property (strong) id<AGImageLocalCacheManager> imageLocalCacheManager;
@end
AssemblyVIPER-модуль
Load image
Massive View Controller -> VIPER
- (AGMainMenuInteractor *)interactorMainMenu { return [TyphoonDefinition withClass:[AGMainMenuInteractor class] configuration:^(TyphoonDefinition *definition) { [definition injectProperty:@selector(imageLocalCacheManager) with:[self.coreHelpersAssembly imageLocalCacheManager]]; }];}
AssemblyVIPER-модуль
Load image
Massive View Controller -> VIPER
- (AGMainMenuInteractor *)interactorMainMenu { return [TyphoonDefinition withClass:[AGMainMenuInteractor class] configuration:^(TyphoonDefinition *definition) { [definition injectProperty:@selector(imageLocalCacheManager) with:[self.coreHelpersAssembly imageLocalCacheManager]]; }];}
AssemblyVIPER-модуль
Load image Configure Module
Massive View Controller -> VIPER
- (id<AGImageLocalCacheManager>)imageLocalCacheManager { return [TyphoonDefinition withClass:[AGImageLocalCacheManagerImplementation class] configuration:^(TyphoonDefinition *definition) { [definition useInitializer:@selector(managerWithFileManager:) parameters:^(TyphoonMethod *initializer) { [initializer injectParameterWith:[self fileManager]]; }]; }];}
CoreHelpersAssembly
Massive View Controller -> VIPER
AssemblyVIPER-модуль
Load image
Конфигурация объектов- (void)loadBackgroundImage { UIImage *guideBackgroundImage = [self.imageLocalCacheManager loadImageWithImageId:guide.backgroundImage.photoId]; [self.output loadedBackgroundImage:guideBackgroundImage];}
Configure Module
Massive View Controller -> VIPER
AssemblyVIPER-модуль
Use Core HelpersLoad image
Конфигурация объектов- (void)loadBackgroundImage { UIImage *guideBackgroundImage = [self.imageLocalCacheManager loadImageWithImageId:guide.backgroundImage.photoId]; [self.output loadedBackgroundImage:guideBackgroundImage];}
Configure Module
- (void)loadBackgroundImage { UIImage *guideBackgroundImage = [self.imageLocalCacheManager loadImageWithImageId:guide.backgroundImage.photoId]; [self.output loadedBackgroundImage:guideBackgroundImage];}
Massive View Controller -> VIPER
Конфигурация объектов
Massive View Controller VIPER
Massive View Controller -> VIPER
View
Interactor
Presenter
Router
Assembly
Massive View Controller -> VIPER
View
Interactor
Presenter
Router
Assembly
Massive View Controller -> VIPER
Трудозатраты
24 человеко-часа
Massive View Controller -> VIPER
Проблемы Massive View Controller
Примеры
Немного статистики
Massive View Controller -> VIPER
Статистика
MVC VIPERR1 ~9,3K / 0,175K ~18K / 0,58KR2 ~7,6K / 0,54K ~1,1K / 0,55KR3 ~3,8K / 0,38K ~8,4K / 0,53K
Massive View Controller -> VIPER
> 1,5 раза
Massive View Controller -> VIPER
Выводы
VIPER MVC
Massive View Controller -> VIPER
Спасибо за внимание!