Upload
krzysztof-profic
View
1.824
Download
0
Tags:
Embed Size (px)
Citation preview
from MVC to VIPER
Krzysztof Profic @kprofic
Legacy codebaseBetter codebase
Pragmatic approachwhat hurts my eyes?
Pragmatic approachwhat hurts my eyes?
UIViewController
Massive UIViewController
4 Steps
• MVC on diet
• MVVM
• Intentions
• VIPER
4 Steps
• MVC on diet
• MVVM
• Intentions
• VIPER
Massive View Controller
Light View Controller
MVC on diet
ReducingMassive ViewController
• extract datasource
• move domain logic into the model
• move view code into the view layer
objc.io #1
ReducingMassive ViewController
• extract datasource
• move domain logic into the model
• move view code into the view layer
objc.io #1
ReducingMassive ViewController
• extract datasource
• move domain logic into the model
• move view code into the view layer
objc.io #1
ReducingMassive ViewController
• Separation of concerns
• Single responsibility principle
objc.io #1
Reanimate your MVCreduce MVC principles violation
put ViewController on a diet
What is the Rule?
“Keep the code where it belongs”
Layer
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
Layersseparation of concerns = dividing software into …
“Keep the code on the right layer”
The Dependency Rule
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
MVC variation
MVC variation
MVVM
4 Steps
• MVC on diet
• MVVM
• Intentions
• VIPER
Let’s talk code
- (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
@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
- (void)viewDidLoad { [super viewDidLoad]; self.nameLabel.text = self.viewModel.nameText; self.birthdateLabel.text = self.viewModel.birthdateText; }
PersonViewController.m
4 Steps
• MVC on diet
• MVVM
• Intentions
• VIPER
Motivation
• ViewController implements only viewWill* viewDid*
• encapsulate small pieces of business logic
• use case approach (form validation, login user)
• hookable via &
• reusable
IntentionsArchitecture is about Intent “Uncle Bob”
IBOutlet IBAction
@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
Observing ViewModel
@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
@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
BehavioursUIControl
objc.io #13
Login
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.
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.
@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
@implementation LoginViewController
- (IBAction)loginIntentionStateChanged:(LoginIntention *)sender { if ([sender.statusLabel.text isEqualToString:@"done"]){ [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } }
@end
LoginViewController.m
4 Steps
• MVC on diet
• MVVM
• Intentions
• VIPER
If you want more, checkout VIPER
• View
• Interactor
• Presenter
• Entity
• Routing
Summary:
MVC on diet MVVM
Intentions / Behaviours VIPER
Thank you!
Krzysztof Profic @kprofic
• 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