O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.
MVVM в реальных 
проектах 
Юрий Буянов 
“Одноклассники”
MVVM
MVC
“Massive View Controller” 
• Занимается вообще всем 
• Сильная связанность с View layer 
• Трудно тестировать 
• Трудно пе...
Model-View-ViewModel 
View 
(+ViewController) Lightweight View 
ViewModel 
Model 
UI logic 
Business logic
Model-View-ViewModel 
View 
(+ViewController) Lightweight View 
ViewModel 
Model 
UI logic 
Business logic 
Bindings!
ViewModel 
• Не знает ничего про ViewController 
• Не использует UIKit 
• Отображает (?) состояние UI с помощью KVO- 
совм...
View 
(ViewController) 
• Декларативно привязывает состояние UI к 
состоянию VM 
• Обрабатывает чистую логику UI (вёрстка,...
Биндинги 
- (void)viewDidLoad 
{ 
[super viewDidLoad]; 
RAC(self.trackTitleLabel, text) = RACObserve(self.viewModel, track...
View 
UI customization 
layout 
animations 
orientation changes 
ViewModel 
Model interaction (network, 
storage) 
Model-r...
Навигация
Ad hoc 
UINavigationController 
Sections 
Controller 
Talks 
Controller 
ctrl = [[TalksController alloc] init] 
2 push 
1 ...
@implementation SectionsController 
- (IBAction)mobileBtnTap:(id)sender { 
UIViewController *ctrl = [[TalksController allo...
Ненужная связанность (coupling) 
@implementation SectionsController 
- (IBAction)mobileBtnTap:(id)sender { 
UIViewControll...
Ad Hoc + MVVM 
UINavigationController 
Sections 
Controller 
Talks Controller 
4 push 
2 
создаёт 
Sections 
ViewModel 
Ta...
@implementation SectionsViewModel 
- (void)mobileBtnPressed { 
MobileTalksViewModel *vm = [[MobileTalksViewModel alloc] in...
@implementation SectionsViewModel 
- (void)mobileBtnPressed { 
MobileTalksViewModel *vm = [[MobileTalksViewModel alloc] in...
Решение “В лоб” 
UINavigationController 
Sections 
Controller 
Sections 2 создаёт 
ViewModel 
Talks 
ViewModel 
1 действие
Решение “В лоб” 
UINavigationController 
Sections 
Controller 
Sections 2 создаёт 
ViewModel 
Talks 
ViewModel 
1 действие...
Решение “В лоб” 
UINavigationController 
Sections 
Controller 
Talks Controller 
Sections 2 создаёт 
ViewModel 
Talks 
Vie...
Решение “В лоб” 
UINavigationController 
Sections 
Controller 
Talks Controller 
5 push 
Sections 2 создаёт 
ViewModel 
Ta...
@implementation SectionsViewModel 
- (void)mobileBtnPressed { 
MobileTalksViewModel *vm = [[MobileTalksViewModel alloc] in...
@implementation SectionsController 
- (IBAction)mobileBtnTap:(id)sender { 
[self.viewModel mobileBtnPressed]; 
} 
- (void)...
Решение “В лоб” 
• Большая степень связанности в VM и 
контроллере 
• Негибкость 
• Повторение кода 
• Отдельный сигнал дл...
Роутер 
(Navigation Object)
MVVM + Роутер 
UINavigationController 
Sections 
Controller 
1 action 2 showTalks 
Sections 
ViewModel
MVC + Роутер 
UINavigationController 
Sections 
Controller 
showDst
Роутер 
• Имеет доступ к навигационным контроллерам (navigation 
controller, tab controller, drawer controller, menu contr...
Роутер 
@interface Router : NSObject 
- (void)showMobileTalks; 
- (void)showWebTalks; 
- (void)showGamesTalks; 
- (void)sh...
Роутер 
@implementation Router 
- (void)showMobileTalks { 
MobileTalksViewModel *vm = [[MobileTalksViewModel alloc] init];...
@implementation SectionsController 
- (IBAction)mobileBtnTap:(id)sender { 
[self.viewModel mobileBtnPressed]; 
} 
- (void)...
@implementation SectionsViewModel 
- (void)mobileBtnPressed { 
[self.router showMobileTalks]; 
} 
@end
Вложенные контроллеры 
UINavigationController 
Controller 
Child controller 
Some other controller 
ViewModel 
Child ViewM...
Вложенные контроллеры 
UINavigationController 
Controller 
Child controller 
Some other controller 
ViewModel 
Child ViewM...
Универсальные 
приложения
Частный случай: 
URL-based роутер 
[self.router showMobileTalks]; 
[self.router open:@"/sections/mobile" modal:NO];
URL-based роутер 
• Легко настраивается 
• Упрощает поддержку URL-схем 
• Упрощает переход от веб-приложений к нативным 
•...
Списки 
(UICollectionView, UITableView)
Модель ячейки 
ViewController ViewModel 
UICollectionView/UITableView 
Cell 
- (void)setViewModel:(MYCellViewModel *)viewM...
Статическая модель 
ячейки 
Cell 
- (void)setViewModel:(OKPTrackCellViewModel *)viewModel { 
NSParameterAssert(viewModel);...
Динамическая модель 
ячейки 
Cell 
- (void)setViewModel:(OKPTrackCellViewModel *)viewModel { 
NSParameterAssert(viewModel)...
Динамическая модель 
ячейки 
Cell 
RAC(self.someIcon, hidden) = RACObserve(viewModel.someFlag).not;
Warning: cell reuse 
Cell 
RAC(self.someIcon, hidden) = RACObserve(viewModel.someFlag).not; 
/// A given key on an object ...
Warning: cell reuse 
Cell 
RAC(self.someIcon, hidden) =[RACObserve(viewModel.someFlag).not 
takeUntil:self.rac_prepareForR...
Ощущения 
(на данном этапе) 
• Технология “на вырост” 
• Сделает возможным написание тестов, но не 
напишет их за вас 
• Х...
Спасибо
#MBLTdev: Опыт использования MVVM в реальных проектах
#MBLTdev: Опыт использования MVVM в реальных проектах
Próximos SlideShares
Carregando em…5
×

#MBLTdev: Опыт использования MVVM в реальных проектах

5.293 visualizações

Publicada em

#MBLTdev: Конференция мобильных разработчиков
Спикер: Юрий Буянов
Разработчик мобильных приложений, Одноклассники
http://mbltdev.ru/

Publicada em: Celular

#MBLTdev: Опыт использования MVVM в реальных проектах

  1. 1. MVVM в реальных проектах Юрий Буянов “Одноклассники”
  2. 2. MVVM
  3. 3. MVC
  4. 4. “Massive View Controller” • Занимается вообще всем • Сильная связанность с View layer • Трудно тестировать • Трудно переиспользовать
  5. 5. Model-View-ViewModel View (+ViewController) Lightweight View ViewModel Model UI logic Business logic
  6. 6. Model-View-ViewModel View (+ViewController) Lightweight View ViewModel Model UI logic Business logic Bindings!
  7. 7. ViewModel • Не знает ничего про ViewController • Не использует UIKit • Отображает (?) состояние UI с помощью KVO- совместимых свойств или RAC-сигналов • Действия представлены в виде методов или RAC- команд • Легко тестируема (и должна тестироваться)
  8. 8. View (ViewController) • Декларативно привязывает состояние UI к состоянию VM • Обрабатывает чистую логику UI (вёрстка, анимации, ориентирование) • Может зависеть от нескольких ViewModel
  9. 9. Биндинги - (void)viewDidLoad { [super viewDidLoad]; RAC(self.trackTitleLabel, text) = RACObserve(self.viewModel, trackTitle); RAC(self.playBtn, enabled) = RACObserve(self.viewModel, loading).not; } - (IBAction)nextBtnTap:(id)sender { [self.viewModel nextBtnPressed]; }
  10. 10. View UI customization layout animations orientation changes ViewModel Model interaction (network, storage) Model-related UI state ??? Navigation Image Loading Action Confirmations UI-only actions …
  11. 11. Навигация
  12. 12. Ad hoc UINavigationController Sections Controller Talks Controller ctrl = [[TalksController alloc] init] 2 push 1 инициализация
  13. 13. @implementation SectionsController - (IBAction)mobileBtnTap:(id)sender { UIViewController *ctrl = [[TalksController alloc] init]; [self.navigationController pushViewController:ctrl animated:YES]; } @end
  14. 14. Ненужная связанность (coupling) @implementation SectionsController - (IBAction)mobileBtnTap:(id)sender { UIViewController *ctrl = [[TalksController alloc] init]; [self.navigationController pushViewController:ctrl animated:YES]; } @end
  15. 15. Ad Hoc + MVVM UINavigationController Sections Controller Talks Controller 4 push 2 создаёт Sections ViewModel Talks ViewModel 1 действие создаёт 3
  16. 16. @implementation SectionsViewModel - (void)mobileBtnPressed { MobileTalksViewModel *vm = [[MobileTalksViewModel alloc] init]; UIViewController *ctrl = [[TalksController alloc] initWithVM:vm]; [self.navigationController pushViewController:ctrl animated:YES]; } @end
  17. 17. @implementation SectionsViewModel - (void)mobileBtnPressed { MobileTalksViewModel *vm = [[MobileTalksViewModel alloc] init]; UIViewController *ctrl = [[TalksController alloc] initWithVM:vm]; [self.navigationController pushViewController:ctrl animated:YES]; } @end Ненужная связанность UI-код внутри ViewModel! :(
  18. 18. Решение “В лоб” UINavigationController Sections Controller Sections 2 создаёт ViewModel Talks ViewModel 1 действие
  19. 19. Решение “В лоб” UINavigationController Sections Controller Sections 2 создаёт ViewModel Talks ViewModel 1 действие 3 сигнал
  20. 20. Решение “В лоб” UINavigationController Sections Controller Talks Controller Sections 2 создаёт ViewModel Talks ViewModel 1 действие 3 сигнал 4 создаёт и связывает
  21. 21. Решение “В лоб” UINavigationController Sections Controller Talks Controller 5 push Sections 2 создаёт ViewModel Talks ViewModel 1 действие 3 сигнал 4 создаёт и связывает
  22. 22. @implementation SectionsViewModel - (void)mobileBtnPressed { MobileTalksViewModel *vm = [[MobileTalksViewModel alloc] init]; [_talksViewModelsSubject sendNext:vm]; } @end
  23. 23. @implementation SectionsController - (IBAction)mobileBtnTap:(id)sender { [self.viewModel mobileBtnPressed]; } - (void)viewDidLoad { [self.viewModel.talksViewModelsSignal subscribeNext:^(id viewModel) { UIViewController* ctrl = [[TalksController alloc] initWithVM:viewModel]; [self.navigationController pushViewController:ctrl animated:YES]; }]; } @end
  24. 24. Решение “В лоб” • Большая степень связанности в VM и контроллере • Негибкость • Повторение кода • Отдельный сигнал для каждого “маршрута”
  25. 25. Роутер (Navigation Object)
  26. 26. MVVM + Роутер UINavigationController Sections Controller 1 action 2 showTalks Sections ViewModel
  27. 27. MVC + Роутер UINavigationController Sections Controller showDst
  28. 28. Роутер • Имеет доступ к навигационным контроллерам (navigation controller, tab controller, drawer controller, menu controller) • Создаёт и конфигурирует экземпляры VM и контроллеров • (сам или используя DI-контейнер) • Осуществляет переходы между экранами • Mockable • Отлично работает в рамках MVVM и MVC
  29. 29. Роутер @interface Router : NSObject - (void)showMobileTalks; - (void)showWebTalks; - (void)showGamesTalks; - (void)showTalk:(Talk*)talk; @end
  30. 30. Роутер @implementation Router - (void)showMobileTalks { MobileTalksViewModel *vm = [[MobileTalksViewModel alloc] init]; UIViewController *ctrl = [[TalksController alloc] initWithVM:vm]; [self.mainNavigationController pushViewController:ctrl animated:YES]; } @end
  31. 31. @implementation SectionsController - (IBAction)mobileBtnTap:(id)sender { [self.viewModel mobileBtnPressed]; } - (void)viewDidLoad { [self.viewModel.talksViewModelsSignal subscribeNext:^(id viewModel) { UIViewController* ctrl = [[TalksController alloc] initWithVM:viewModel]; [self.navigationController pushViewController:ctrl animated:YES]; }]; } @end
  32. 32. @implementation SectionsViewModel - (void)mobileBtnPressed { [self.router showMobileTalks]; } @end
  33. 33. Вложенные контроллеры UINavigationController Controller Child controller Some other controller ViewModel Child ViewModel Some other ViewModel
  34. 34. Вложенные контроллеры UINavigationController Controller Child controller Some other controller ViewModel Child ViewModel Some other ViewModel
  35. 35. Универсальные приложения
  36. 36. Частный случай: URL-based роутер [self.router showMobileTalks]; [self.router open:@"/sections/mobile" modal:NO];
  37. 37. URL-based роутер • Легко настраивается • Упрощает поддержку URL-схем • Упрощает переход от веб-приложений к нативным • Логика навигации может “утекать” в VM/контроллер • Сложности с передачей сложных объектов • Подходит не для всех приложений
  38. 38. Списки (UICollectionView, UITableView)
  39. 39. Модель ячейки ViewController ViewModel UICollectionView/UITableView Cell - (void)setViewModel:(MYCellViewModel *)viewModel CellViewModel CellViewModel CellViewModel CellViewModel - (void)viewDidLoad { [super viewDidLoad]; [self.viewModel.updatedContentSignal subscribeNext:^(id x) { [self.collectionView reloadData]; }]; } Cell Cell @property RACSubject *updatedContentSignal; NSArray *cellViewModels
  40. 40. Статическая модель ячейки Cell - (void)setViewModel:(OKPTrackCellViewModel *)viewModel { NSParameterAssert(viewModel); self.titleLabel.text = viewModel.title; self.subTitleLabel.text = viewModel.subtitle; self.someIcon.hidden = !viewModel.someFlag; Cell ViewModel } @property NSString* title; @property NSString* subtitle; @property BOOL someFlag;
  41. 41. Динамическая модель ячейки Cell - (void)setViewModel:(OKPTrackCellViewModel *)viewModel { NSParameterAssert(viewModel); self.titleLabel.text = viewModel.title; self.subTitleLabel.text = viewModel.subtitle; self.someIcon.hidden = !viewModel.someFlag; Cell ViewModel } @property NSString* title; @property NSString* subtitle; @property BOOL someFlag; //Может изменяться!
  42. 42. Динамическая модель ячейки Cell RAC(self.someIcon, hidden) = RACObserve(viewModel.someFlag).not;
  43. 43. Warning: cell reuse Cell RAC(self.someIcon, hidden) = RACObserve(viewModel.someFlag).not; /// A given key on an object should only have one active signal bound to it at any given time. Binding more than one signal to the same property is considered undefined behavior.
  44. 44. Warning: cell reuse Cell RAC(self.someIcon, hidden) =[RACObserve(viewModel.someFlag).not takeUntil:self.rac_prepareForReuseSignal]; /// Solution: unsubscribe from RACObserve signal on cell reuse
  45. 45. Ощущения (на данном этапе) • Технология “на вырост” • Сделает возможным написание тестов, но не напишет их за вас • Хинт: используйте Dependency Injection. • Вам придётся полюбить ReactiveCocoa
  46. 46. Спасибо

×