22
Introduction to VIPER Architecture Hendy Christianto

Introduction to VIPER Architecture

Embed Size (px)

Citation preview

Page 1: Introduction to VIPER Architecture

Introduction to VIPER Architecture

Hendy Christianto

Page 2: Introduction to VIPER Architecture

Why VIPER?• MVC - Massive View Controller

• What goes into View Controllers?

• Data sources for Views (UITableViews)

• Business Logic

• Application Flows

• Transition between view controller

Page 3: Introduction to VIPER Architecture

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

Page 4: Introduction to VIPER Architecture

Architecture of VIPER

Page 5: Introduction to VIPER Architecture

View

• The View is passive

• Waits for the Presenter to give content to display

• Detect user interaction

• The view is an abstract interface

Page 6: Introduction to VIPER Architecture

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:

Page 7: Introduction to VIPER Architecture

Presenter

• Tells the view what to display

• Handle events

Page 8: Introduction to VIPER Architecture

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]; }

Presenter View

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

Page 9: Introduction to VIPER Architecture

Interactor

• Perform business logic

• Carry out events that notified by presenter (input)

• Produce output and notify back the presenter

Page 10: Introduction to VIPER Architecture

Interactor Cont’d

Interactor Presenter 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)

Page 11: Introduction to VIPER Architecture

Data Manager

• Fetch data from database

• Restructure data to model / entities

• Store data

Page 12: Introduction to VIPER Architecture

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:

Page 13: Introduction to VIPER Architecture

Service

• Execute requests related to Entities / Models

• Network/ API, Database (local)

Page 14: Introduction to VIPER Architecture

Entities

• Represent data

• Passed between class

Page 15: Introduction to VIPER Architecture

Entities Cont’d

@interface User : NSManagedObject

@property (nonatomic, strong) NSString *email; @property (nonatomic, strong) NSString *name;

@end

@implementation User

@dynamic name; @dynamic email;

@end

Page 16: Introduction to VIPER Architecture

Wireframe

• Initialize view controllers, Presenter, Interactor

• Handles routing / navigation within Views

Page 17: Introduction to VIPER Architecture

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; }

Page 18: Introduction to VIPER Architecture

Benefits of Viper

• Easy to iterate on

• Collaboration-friendly

• Separated concerns

• Easy to test

Page 19: Introduction to VIPER Architecture

Conclusion

• Helps developer to be more explicit about separation of code

• Single responsibility each class, easier to maintain

• Neat Code!!

Page 20: Introduction to VIPER Architecture

References• http://mutualmobile.github.io/blog/2013/12/04/viper-

introduction/

• http://www.objc.io/issue-13/viper.html

• https://medium.com/brigade-engineering/brigades-experience-using-an-mvc-alternative-36ef1601a41f

• http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

• iOS viper presentation - http://www.slideshare.net/RajatDatta1/i-os-viper-presentation

Page 21: Introduction to VIPER Architecture

DEMOS

Page 22: Introduction to VIPER Architecture

Q&A