Contenu connexe Similaire à Introduction to VIPER Architecture (20) Introduction to VIPER Architecture2. Why VIPER?
• MVC - Massive View Controller
• What goes into View Controllers?
• Data sources for Views (UITableViews)
• Business Logic
• Application Flows
• Transition between view controller
3. What is VIPER?
• Based on clean architecture to Uncle Bob’s
clean architecture.
• Basically a new architecture, introduced on 2014
• Used to resolve “Massive View Controller”
• Using “Single Responsibility” as a principle
5. View
• The View is passive
• Waits for the Presenter to give content to display
• Detect user interaction
• The view is an abstract interface
6. View Cont’d
@protocol SignInEmailView <NSObject>
- (void)setEmailLabelText:(NSString *)email;
- (void)setPasswordLabelText:(NSString *)password;
@end
#import <UIKit/UIKit.h>
#import "SignInEmailView.h"
@interface ViewController : UIViewController <SignInEmailView>
@end
only protocols, abstract interface
implements SignInEmailView interface
View Presenter
setPasswordLabelText:
8. Presenter Cont’d
@implementation ViewController
#pragma mark -
#pragma mark View Interface
- (void)setEmailLabelText:(NSString *)email {
self.emailLabel.text = email;
}
- (void)setPasswordLabelText:(NSString *)password {
self.passwordLabel.text = password;
}
- (void)setErrorMessage:(NSString *)error {
self.errorLabel.text = error;
}
#pragma mark -
#pragma mark IBAction
- (IBAction)buttonLoginClicked:(id)sender {
[self.presenter didLoginWithEmail:self.emailTextField.text
password:self.passwordTextField.text];
}
PresenterView
buttonLoginClicked:
didLoginWithEmail: password:
Interactor
signInWithEmail:password:
@implementation SignInPresenter
#pragma mark -
#pragma mark Wireframe Update UI
- (void)presentSignInEmailView {
[self.view setPasswordLabelText:@"Password"];
[self.view setEmailLabelText:@"Email"];
}
- (void)didLoginWithEmail:(NSString *)email password:(NSString *)password {
[self.interactor signInWithEmail:email password:password];
}
@end
#import <Foundation/Foundation.h>
#import "SignInEmailView.h"
#import "SignInInteractorIO.h"
@interface SignInPresenter : NSObject
@property (nonatomic, weak) id <SignInEmailView> view;
@property (nonatomic, strong) id <SignInInteractorInput> interactor;
- (void)presentSignInEmailView;
- (void)didLoginWithEmail:(NSString *)email password:(NSString *)password;
@end
10. Interactor Cont’d
InteractorPresenter
signInWithEmail:password:
didSignInWithResponse:
@implementation SignInPresenter
#pragma mark -
#pragma mark Wireframe Update UI
- (void)presentSignInEmailView {
[self.view setPasswordLabelText:@"Password"];
[self.view setEmailLabelText:@"Email"];
}
- (void)didLoginWithEmail:(NSString *)email password:(NSString *)password {
[self.interactor signInWithEmail:email password:password];
}
#pragma mark -
#pragma mark Interactor
- (void)didSignInWithResponse:(NSDictionary *)response {
NSError *error = response[@"error"];
if (error) {
[self.view setErrorMessage:error.domain];
} else {
[self.signInWireframe pushWelcomeView];
}
}
@interface SignInInteractor ()
@property (nonatomic, strong) SignInDataManager *dataManager;
@end
@implementation SignInInteractor
- (void)signInWithEmail:(NSString *)email password:(NSString *)password {
[self.dataManager userWithEmail:email password:password completion:^(User *user) {
NSMutableDictionary *response = [NSMutableDictionary new];
if (user) {
response[@"user"] = user;
} else {
response[@"error"] = [NSError errorWithDomain:@"User Not Found"
code:404 userInfo:nil];
}
[self.output didSignInWithResponse:response];
}];
}
@end
@interface SignInInteractor : NSObject <SignInInteractorInput>
@property (nonatomic, weak) id <SignInInteractorOutput> output;
@end
@protocol SignInInteractorInput <NSObject>
- (void)signInWithEmail:(NSString *)email password:(NSString *)password;
@end
@protocol SignInInteractorOutput <NSObject>
- (void)didSignInWithResponse:(NSDictionary *)response;
Data
Manager
userWithEmail:password:completion:
completion:^(User *user)
12. Data Manager Cont’d
Data
Manager
Interactor
@interface SignInInteractor ()
@property (nonatomic, strong) SignInDataManager *dataManager;
@end
@implementation SignInInteractor
- (void)signInWithEmail:(NSString *)email password:(NSString *)password {
[self.dataManager userWithEmail:email password:password completion:^(User *user) {
NSMutableDictionary *response = [NSMutableDictionary new];
if (user) {
response[@"user"] = user;
} else {
response[@"error"] = [NSError errorWithDomain:@"User Not Found"
code:404 userInfo:nil];
}
[self.output didSignInWithResponse:response];
}];
}
@end
Service
userWithEmail:password:completion:
@implementation SignInDataManager
- (void)userWithEmail:(NSString *)email password:(NSString *)password
completion:(void (^)(User *))completionBlock {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(email == %@)", email];
[self.dataStore fetchEntriesWithPredicate:predicate sortDescriptors:@[]
completionBlock:^(NSArray *results) {
if (completionBlock) {
if (results.count == 0) {
completionBlock(nil);
} else {
completionBlock(results[0]);
}
}
}];
}
@end
fetchEntriesWithPredicate:sortDescriptors:completionBlock
results arrayrun completionBlock:
15. Entities Cont’d
@interface User : NSManagedObject
@property (nonatomic, strong) NSString *email;
@property (nonatomic, strong) NSString *name;
@end
@implementation User
@dynamic name;
@dynamic email;
@end
17. Wireframe Cont’d
@class SignInPresenter;
@class WelcomeWireframe;
@interface SignInWireframe : RootWireframe
@property (nonatomic, strong) WelcomeWireframe *welcomeWireframe;
- (void)pushWelcomeView;
- (void)presentSignInViewControllerOnWindow:(UIWindow *)window;
@end
static NSString *ViewControllerIdentifier = @"ViewController";
@interface SignInWireframe ()
@property (nonatomic, strong) SignInPresenter *presenter;
@property (nonatomic, strong) SignInInteractor *interactor;
@end
@implementation SignInWireframe
- (void)initializeClasses {
self.presenter = [[SignInPresenter alloc] init];
self.interactor = [[SignInInteractor alloc] init];
self.presenter.interactor = self.interactor;
self.interactor.output = self.presenter;
}
- (void)presentSignInViewControllerOnWindow:(UIWindow *)window {
[self initializeClasses];
ViewController *signInVC = [self signInViewController];
signInVC.presenter = self.presenter;
self.presenter.view = signInVC;
[self createNavigationControllerWithRootView:signInVC];
window.rootViewController = self.navigationController;
}
- (void)pushWelcomeView {
[self.welcomeWireframe pushWelcomeViewControllerOnNavigation:self.navigationController];
}
- (ViewController *)signInViewController {
UIStoryboard *storyboard = [self mainStoryboard];
ViewController *signInVC = [storyboard instantiateViewControllerWithIdentifier:ViewControllerIdentifier];
return signInVC;
}
18. Benefits of Viper
• Easy to iterate on
• Collaboration-friendly
• Separated concerns
• Easy to test
19. Conclusion
• Helps developer to be more explicit about
separation of code
• Single responsibility each class, easier to
maintain
• Neat Code!!