Upload
rambler-ios
View
8.950
Download
0
Embed Size (px)
Citation preview
Создание модульных приложений на примере
“Рамблер.Кассы”
О чем?
• Введение в историю “Рамблер.Касса”
• Что используем
• Пример
О чем?
• Введение в историю “Рамблер.Касса”
• Что используем
• Пример
О чем?
• Введение в историю “Рамблер.Касса”
• Что используем
• Пример
Модуль — функционально законченный фрагмент программы, оформленный в виде отдельного файла с исходным кодом.
Глоссарий
Модульность — это свойство системы, связанное с возможностью ее декомпозиции на ряд внутренне связанных между собой модулей
Глоссарий
Немного истории
• Приложение, отображающее информацию по событию: - кино - спектакли - спорт
• Возможность купить билеты на эти события без наценки
• iPhone и iPad версии
Что же такое “Рамблер.Касса”?
Немного истории
• Приложение, отображающее информацию по событию: - кино - спектакли - спорт
• Возможность купить билеты на эти события без наценки
• iPhone и iPad версии
Что же такое “Рамблер.Касса”?
Немного истории
• Приложение, отображающее информацию по событию: - кино - спектакли - спорт
• Возможность купить билеты на эти события без наценки
• iPhone и iPad версии
Что же такое “Рамблер.Касса”?
Немного истории
• Приложение, отображающее информацию по событию: - кино - спектакли - спорт
• Возможность купить билеты на эти события без наценки
• iPhone и iPad версии
Что же такое “Рамблер.Касса”?
Немного истории
Что же такое “Рамблер.Касса”?
Немного истории
Что же такое “Рамблер.Касса”?
Немного истории
• Проект развивается
• Появляется первая “брендированная версия”
Немного истории
Что же такое “брендированная” версия?
• Отдельное приложение, существующее обособленно от основной версии
• Имеет свои особенности: - своя цветовая схема, логотипы и т.д. - отличный от основной кассы функционал
Немного истории
Что же такое “брендированная” версия?
Немного истории
Что же такое “брендированная” версия?
Немного истории
• Общий функционал
• Отличный функционал
• Количество версий только растет
Проблема
Проблема
Проблема
Расписание
Поддержка Билеты
Карта
Спорт
Кино Кино
Кино
Проблема
Расписание
Билеты
Карта
Расписание
Поддержка
Билеты
Как это было?
• Один проект для всех версий
• Множество таргетов
• Ветвления через “if” по define макросам
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { #ifdef BOLSHOI switch (section) { case 2: return 2; case 3: return 2; default: return 0; } #else switch (section) { case 0: return 2; case 1: return 1;
default: return 0; } #endif }
Как это было?
- (instancetype)init { self = [super init]; NSString *bundleIdentifier = [[NSBundle mainBundle] infoDictionary][@"CFBundleIdentifier"]; if ([bundleIdentifier rangeOfString:@"cinemapark"].location != NSNotFound) { // CINEMA PARK _tintColor = [UIColor colorWithRed:31.f / 255.f green:110.f / 255.f blue:180.f / 255.f alpha:1.f]; _navigationBarTintColor = [UIColor colorWithRed:4.f/255.f green:110.f/255.f blue:180.f/255.f alpha:1.f]; _navigationBarTitleColor = [UIColor whiteColor]; _shouldShowTheaters = NO; _onlyOneCinema = NO; #if !defined(AF_APP_EXTENSIONS) [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; #endif if ([bundleIdentifier hasSuffix:@".dev"]) { _widgetKey = @"foo"; } else { _widgetKey = @"foo"; } } else if ([bundleIdentifier rangeOfString:@"Bolshoi"].location != NSNotFound) { // Bolshoi Rostov _tintColor = [UIColor kassaBolshoiMainColor]; _navigationBarTintColor = [UIColor kassaBolshoiMainColor]; _navigationBarTitleColor = [UIColor kassaBolshoiActiveTabsColor]; _selectedBarItemColor = [UIColor kassaBolshoiActiveTabsColor]; _unselectedBarItemColor = [UIColor kassaBolshoiSecondaryColor]; _barTextColor = [UIColor kassaBolshoiActiveTabsColor]; _shouldShowTheaters = NO; _onlyOneCinema = YES; if ([bundleIdentifier hasSuffix:@".dev"]) { _widgetKey = @"foo"; } else { _widgetKey = @"foo"; } }else { // KASSA APP _tintColor = [UIColor colorWithRed:255.f / 255.f green:133.f / 255.f blue:47.f / 255.f alpha:1.f]; _navigationBarTintColor = nil; _navigationBarTitleColor = nil; _selectedBarItemColor = _tintColor; _unselectedBarItemColor = nil; _barTextColor = nil; _shouldShowTheaters = YES; _onlyOneCinema = NO; if ([bundleIdentifier hasSuffix:@".dev"]) { _widgetKey = @"foo"; } else { _widgetKey = @"foo"; } } return self; }
if ([bundleIdentifier rangeOfString:@"cinemapark"].location != NSNotFound) { // CINEMA PARK _tintColor = [UIColor colorWithRed:31.f / 255.f green:110.f / 255.f blue:180.f / 255.f alpha:1.f];
Как это было?
Как это было?
Решение
• Модуль - экран приложения, выполняющий свою функцию
• Саб-модуль - отдельная функциональность конкретного модуля, отвечающая за единственную обязанность
Решение
• Разбить приложение на модули
• Разбить каждые модули на саб-модули
• Подключать нужные модули, а с ними и нужные саб-модули для конкретных таргетов
Решение
Решение
Модуль “кинотеатры”
• Получает через API список кинотеатров
• Отображает этот список
• Осуществляет навигацию на другие модули
Решение
Модуль “кинотеатры”
view presenter
router
interactor
entity
entity
entity
Решение
Модуль “кинотеатры”
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
Решение
Модуль “кинотеатры”
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
Решение
Модуль “кинотеатры”
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
Решение
Модуль “кинотеатры”
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
Решение
Модуль “кинотеатры”
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
Решение
Модуль “кинотеатры”
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
Решение
Модуль “кинотеатры”
Nimbus
Решение
Nimbus
Cell Object
Решение
Nimbus
Cell ObjectСконфигурированная
ячейка
Решение
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
Решение
Модуль “кинотеатры”
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
Решение
Модуль “кинотеатры”
TYPHOON
Решение
Решение@interface RKCinemasDefaultConfiguratorAssembly : TyphoonAssembly
@property (nonatomic, strong) RKSharedAssembly *sharedAssembly;
- (id<RKCinemasViewProtocol, RKBaseListViewOutConfiguratorProtocol, RKCinemasDataProviderDelegate>)cinemasViewController; - (id<RKCinemasPresenterViewOutputProtocol, RKCinemasPresenterInteractorOutputProtocol>)cinemaPresenter; - (id<RKCinemasInteractorProtocol>)cinemasInteractor; - (id<RKCinemasRouterProtocol>)cinemasRouter; - (id<RKCinemasServiceProtocol>)cinemasService; - (id<RKBaseListViewConfiguratorProtocol>)cinemaConfigurator; - (id<RKCinemasDataSourceProviderProtocol>)cinemasDataSourceProvider; - (id<RKCinemasDataSourceConverterProtocol>)cinemasDataConverter;
@end
Решение
Активация фабрики
Решение
Решение
Решение
Модуль “кинотеатры”
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
Решение
Модуль “кинотеатры”
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
RKCinemasDataConverter
Решение
Модуль “кинотеатры”
view presenter
router
interactor entityservice
data source provider
data source converter
ui configurator
RKCinemasDataConverterRKCinemasGiantDataConverter
Решение
Модуль “кинотеатры”
Решение@implementation RKCinemasDataConverter
- (NSArray *)convertArrayOfListCinemasToPlaceCellObjects:(NSArray *)list { NSMutableArray *array = [[NSMutableArray alloc] init]; for (RKList *listObject in list) { [array addObject:listObject.title]; NSArray *objects = [self convertListOfCinemasToCellObjects:listObject.objects]; [array addObjectsFromArray:objects]; } return [array copy]; }
- (NSArray *)convertListOfCinemasToCellObjects:(NSArray *)list { NSMutableArray *array = [[NSMutableArray alloc] init]; for (RKPlace *place in list) { [array addObject:[RKListPlaceCellObject objectWithPlace:place]]; } return [array copy]; }
@end
Решение@implementation RKCinemasGiantDataConverter
- (NSArray *)convertListOfCinemasToCellObjects:(NSArray *)list { NSMutableArray *array = [[NSMutableArray alloc] init]; for (RKPlace *place in list) { id<RKCinemaDataProtocol>cinemaInfo = [self.dataProvider cinemaInfoById:place.identifier]; NSString *backgroundImageName = [cinemaInfo backgroundImageName]; NSString *logoImageName = [cinemaInfo logoImageName]; [array addObject:[RKPhotoCinemaCellObject objectWithPlace:place backgroundImageName:backgroundImageName logoImageName:logoImageName]]; } return [array copy]; }
- (NSArray *)convertArrayOfListCinemasToPlaceCellObjects:(NSArray *)list { NSMutableArray *array = [[NSMutableArray alloc] init];
for (RKList *listObject in list) { [array addObject:listObject.title]; NSArray *objects = [self convertListOfCinemasToCellObjects:listObject.objects]; [array addObjectsFromArray:objects]; } return [array copy]; }
@end
Решение
#import "RKCinemasDefaultConfiguratorAssembly.h"
@protocol RKCinemasDataProviderProtocol;
@interface RKCinemasGiantConfiguratorAssembly : RKCinemasDefaultConfiguratorAssembly
- (id<RKCinemasDataSourceConverterProtocol>)cinemasDataConverter; - (id<RKCinemasDataProviderProtocol>)giantCinemasDataProvider;
@end
Решение
- (id<RKCinemasDataSourceConverterProtocol>)cinemasDataConverter { return [TyphoonDefinition withClass:[RKCinemasGiantDataConverter class] configuration:^(TyphoonDefinition *definition) { [definition injectProperty:@selector(dataProvider) with:[self giantCinemasDataProvider]]; }]; }
- (id<RKCinemasDataSourceConverterProtocol>)cinemasDataConverter { return [TyphoonDefinition withClass:[RKCinemasDataConverter class]]; }
Definition для основной версии
Definition для “брединрованной” версии
Решениеrambler-kassa.plist
giant.plist
Решение
Заключение
• Код чище
• Уникальный функционал затрагивает только нужные таргеты
• Общий код для всех таргетов
• Тестирование саб-модулей
Заключение
• Код чище
• Уникальный функционал затрагивает только нужные таргеты
• Общий код для всех таргетов
• Тестирование саб-модулей
Заключение
• Код чище
• Уникальный функционал затрагивает только нужные таргеты
• Общий код для всех таргетов
• Тестирование саб-модулей
Заключение
• Код чище
• Уникальный функционал затрагивает только нужные таргеты
• Общий код для всех таргетов
• Тестирование саб-модулей
Заключение
• VIPER
• NIMBUS
• TYPHOON
Заключение• COCOAPOD (?)
Ссылки
http://typhoonframework.org/
Статья с общими принципами
https://www.objc.io/issues/13-architecture/viper/
Официальный сайт проекта “Тyphoon”
http://habrahabr.ru/company/rambler-co/blog/264683/
https://www.youtube.com/watch?v=LO59z3fjc9k
TYPHOON
Цикл статей от Егора Толстова
Выступление на rambler.ios #3
VIPER
https://medium.com/brigade-engineering/brigades-experience-using-an-mvc-alternative-36ef1601a41f
Вводная статья
NIMBUShttps://github.com/jverkoey/nimbus Исходники и ссылка на wiki проекта
http://www.slideshare.net/Rambler-iOS/nimbus-models Презентация с rambler.ios #1 от Стаса Цыганова