from mvc to viper
TRANSCRIPT
![Page 1: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/1.jpg)
from MVC to VIPER
Krzysztof Profic @kprofic
![Page 2: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/2.jpg)
Legacy codebaseBetter codebase
![Page 3: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/3.jpg)
Pragmatic approachwhat hurts my eyes?
![Page 4: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/4.jpg)
Pragmatic approachwhat hurts my eyes?
UIViewController
![Page 5: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/5.jpg)
Massive UIViewController
![Page 6: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/6.jpg)
4 Steps
• MVC on diet
• MVVM
• Intentions
• VIPER
![Page 7: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/7.jpg)
4 Steps
• MVC on diet
• MVVM
• Intentions
• VIPER
![Page 8: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/8.jpg)
Massive View Controller
Light View Controller
MVC on diet
![Page 9: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/9.jpg)
ReducingMassive ViewController
• extract datasource
• move domain logic into the model
• move view code into the view layer
objc.io #1
![Page 10: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/10.jpg)
ReducingMassive ViewController
• extract datasource
• move domain logic into the model
• move view code into the view layer
objc.io #1
![Page 11: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/11.jpg)
ReducingMassive ViewController
• extract datasource
• move domain logic into the model
• move view code into the view layer
objc.io #1
![Page 12: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/12.jpg)
ReducingMassive ViewController
• Separation of concerns
• Single responsibility principle
objc.io #1
![Page 13: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/13.jpg)
Reanimate your MVCreduce MVC principles violation
put ViewController on a diet
![Page 14: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/14.jpg)
What is the Rule?
![Page 15: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/15.jpg)
“Keep the code where it belongs”
![Page 16: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/16.jpg)
Layer
![Page 17: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/17.jpg)
![Page 18: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/18.jpg)
![Page 19: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/19.jpg)
Entities
Use Cases
Controllers
Presen
ters
GatewaysUI
DB
ExternalInterfaces
Device
s
The Clean Architecture
Enterprise Business Rules
Application Business Rules
Interface Adapters
Frameworks & Drivers
Web
Controller
Use CaseInteractor
Presenter Use CaseOutput Port
Use CaseInput Port
Flow of control
<I>
<I>
http://blog.8thlight.com
![Page 20: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/20.jpg)
Layersseparation of concerns = dividing software into …
![Page 21: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/21.jpg)
“Keep the code on the right layer”
![Page 22: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/22.jpg)
The Dependency Rule
![Page 23: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/23.jpg)
Entities
Use Cases
Controllers
Presen
ters
GatewaysUI
DB
ExternalInterfaces
Device
s
The Clean Architecture
Enterprise Business Rules
Application Business Rules
Interface Adapters
Frameworks & Drivers
Web
Controller
Use CaseInteractor
Presenter Use CaseOutput Port
Use CaseInput Port
Flow of control
<I>
<I>
http://blog.8thlight.com
![Page 24: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/24.jpg)
MVC variation
![Page 25: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/25.jpg)
MVC variation
![Page 26: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/26.jpg)
MVVM
![Page 27: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/27.jpg)
4 Steps
• MVC on diet
• MVVM
• Intentions
• VIPER
![Page 28: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/28.jpg)
Let’s talk code
![Page 29: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/29.jpg)
- (void)viewDidLoad { [super viewDidLoad]; if (self.model.salutation.length > 0) { self.nameLabel.text = [NSString stringWithFormat:@"%@ %@ %@“, self.model.salutation, self.model.firstName, self.model.lastName]; } else { self.nameLabel.text = [NSString stringWithFormat:@"%@ %@“, self.model.firstName, self.model.lastName]; } NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"EEEE MMMM d, yyyy"]; self.birthdateLabel.text = [dateFormatter stringFromDate:model.birthdate]; }
PersonViewController.m
![Page 30: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/30.jpg)
@implementation PersonViewModel
- (instancetype)initWithPerson:(Person *)person { self = [super init]; if (!self) return nil; _person = person; if (person.salutation.length > 0) { _nameText = [NSString stringWithFormat:@"%@ %@ %@“, self.person.salutation, self.person.firstName, self.person.lastName]; } else { _nameText = [NSString stringWithFormat:@"%@ %@", self.person.firstName, self.person.lastName]; } NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"EEEE MMMM d, yyyy"]; _birthdateText = [dateFormatter stringFromDate:person.birthdate]; return self; }
@end PersonViewModel.m
![Page 31: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/31.jpg)
- (void)viewDidLoad { [super viewDidLoad]; self.nameLabel.text = self.viewModel.nameText; self.birthdateLabel.text = self.viewModel.birthdateText; }
PersonViewController.m
![Page 32: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/32.jpg)
4 Steps
• MVC on diet
• MVVM
• Intentions
• VIPER
![Page 33: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/33.jpg)
Motivation
• ViewController implements only viewWill* viewDid*
![Page 34: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/34.jpg)
• encapsulate small pieces of business logic
• use case approach (form validation, login user)
• hookable via &
• reusable
IntentionsArchitecture is about Intent “Uncle Bob”
IBOutlet IBAction
![Page 35: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/35.jpg)
![Page 36: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/36.jpg)
@interface ViewController ()
@property (strong) IBOutlet ModelContainer* modelContainer;
@end
@implementation ViewController
- (void)viewDidLoad { [super viewDidLoad]; PersonViewModel * pvm = [[PersonViewModel alloc] initWithModel:self.person]; self.modelContainer.viewModel = pvm; } @end
ViewController.m
![Page 37: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/37.jpg)
Observing ViewModel
![Page 38: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/38.jpg)
@interface ObserveIntention ()
@property (strong, nonatomic) IBOutlet id sourceObject; @property (strong, nonatomic) IBOutlet id target; @property (copy, nonatomic) IBOutlet NSString *sourceKeyPath; @property (copy, nonatomic) IBOutlet NSString *targetKeyPath;
@end
![Page 39: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/39.jpg)
@implementation ObserveIntention
- (void)awakeFromNib { [super awakeFromNib]; [self updateValue]; [self.sourceObject addObserver:self forKeyPath:self.sourceKeyPath options:0 context:nil]; }
- (void)updateValue { id value = [self.sourceObject valueForKeyPath: self.sourceKeyPath]; if (self.targetKeyPath) { [self.target setValue:value forKeyPath:self.targetKeyPath]; } }
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)obj change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:self.sourceKeyPath]) { [self updateValue]; } }
@end
![Page 40: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/40.jpg)
![Page 41: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/41.jpg)
BehavioursUIControl
objc.io #13
![Page 42: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/42.jpg)
Login
![Page 43: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/43.jpg)
User Story #90.10 - Login
• A. When I as a user open the app the first time I enter the “main login” page, where I need to login with my username (email address) and password.
• B. When I tap the “login button” while online and no errors occur I’m moved to initial page with users profile.
![Page 44: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/44.jpg)
User Story #90.10 - Login
• A. When I as a user open the app the first time I enter the “main login” page, where I need to login with my username (email address) and password.
• B. When I tap the “login button” while online and no errors occur I’m moved to initial page with users profile.
![Page 45: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/45.jpg)
![Page 46: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/46.jpg)
@interface LoginIntention() @property (strong) IBOutlet UITextField * usernameTextField; @property (strong) IBOutlet UITextField * passwordTextField; @property (strong, nonatomic) IBOutlet UILabel * statusLabel; @end
@implementation LoginIntention
- (IBAction)login:(id)sender { self.statusLabel.text = @"connecting..."; [self sendActionsForControlEvents:UIControlEventValueChanged]; self.statusLabel.text = [NSString stringWithFormat:@"Authenticating %@“, self.usernameTextField.text]; [self sendActionsForControlEvents:UIControlEventValueChanged]; self.statusLabel.text = @"done"; [self sendActionsForControlEvents:UIControlEventValueChanged]; }
@end
LoginIntention.m
![Page 47: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/47.jpg)
@implementation LoginViewController
- (IBAction)loginIntentionStateChanged:(LoginIntention *)sender { if ([sender.statusLabel.text isEqualToString:@"done"]){ [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } }
@end
LoginViewController.m
![Page 48: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/48.jpg)
4 Steps
• MVC on diet
• MVVM
• Intentions
• VIPER
![Page 49: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/49.jpg)
If you want more, checkout VIPER
• View
• Interactor
• Presenter
• Entity
• Routing
![Page 50: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/50.jpg)
Summary:
MVC on diet MVVM
Intentions / Behaviours VIPER
![Page 51: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/51.jpg)
Thank you!
Krzysztof Profic @kprofic
![Page 52: From mvc to viper](https://reader035.vdocuments.us/reader035/viewer/2022062419/55a2cb081a28ab0e6c8b47c7/html5/thumbnails/52.jpg)
• http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
• http://www.scottlogic.com/blog/2014/05/11/reactivecocoa-tableview-binding.html
• http://chris.eidhof.nl/posts/intentions.html
• http://www.objc.io/issue-13/behaviors.html
• http://www.objc.io/issue-13/viper.html