Rambler.iOS #4: Создание модульных приложений на примере...

Post on 15-Apr-2017

8.950 views 0 download

Transcript of Rambler.iOS #4: Создание модульных приложений на примере...

Создание модульных приложений на примере

“Рамблер.Кассы”

О чем?

• Введение в историю “Рамблер.Касса”

• Что используем

• Пример

О чем?

• Введение в историю “Рамблер.Касса”

• Что используем

• Пример

О чем?

• Введение в историю “Рамблер.Касса”

• Что используем

• Пример

Модуль — функционально законченный фрагмент программы, оформленный в виде отдельного файла с исходным кодом.

Глоссарий

Модульность — это свойство системы, связанное с возможностью ее декомпозиции на ряд внутренне связанных между собой модулей

Глоссарий

Немного истории

• Приложение, отображающее информацию по событию: - кино - спектакли - спорт

• Возможность купить билеты на эти события без наценки

• 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 от Стаса Цыганова