Что такое VIPER?
VIPER применяется в iOS приложениях, построенных на так называемой Clean Architecture. Мир VIPER основан на View, Interactor, Presenter, Entity, и Routing. Clean Architecture делит логику приложения на отдельные уровни ответственности, которые показаны ниже.
Рассмотрим главные части архитектуры подробнее:
View - Отображает то, что ему говорит Presenter и возвращает обратно данные тому же Presenter, введенные пользователем.
Interactor- Включает в себя всю бизнес-логику.
Presenter- Включает в себя View логику подготовки данных для отображения и реагирует на обратные действия пользователя.
Entity- Включает в себя базовую модель объектов, которые использует Interactor.
Routing- Отвечает за логику навигации, какие экраны показывать и в каком порядке.
Это разделение так же соответствует принципу “единоличная ответственность” или Single Responsibility Principle. То есть Interactor можно представить как бизнес-аналитика, Presenter взаимодействует с дизайнером, а View отвечает за визуального-дизайнера.
Ниже приведена схема, где показано как отдельные части архитектуры связаны между собой:
Interactor
Представляет собой как единственный экземпляр в iOS приложении. Он включает в себя работу бизнес-логику манипулирования моделей объектов и не выходит за другие рамки задач. Работать с Interactor довольно удобно, потому что он может быть использован как и в iOS приложении, так и в OS X.
- (void)findUpcomingItems
{
__weak typeof(self) welf = self;
NSDate* today = [self.clock today];
NSDate* endOfNextWeek = [[NSCalendar currentCalendar] dateForEndOfFollowingWeekWithDate:today];
[self.dataManager todoItemsBetweenStartDate:today endDate:endOfNextWeek completionBlock:^(NSArray* todoItems) {
[welf.output foundUpcomingItems:[welf upcomingItemsFromToDoItems:todoItems]];
}];
}
Entity
Объекты моделей, которые обрабатывает Interactor. Сущности могут взаимодействовать только с Interactor. Interactor никогда не передает сущности уровню отображения (т.е. Presenter). Сущности так же склонны к термину PONSOs, и если вы используете Core Data, вы нуждаетесь в управляемых объектах ваших данных. Interactors не должны работать с NSManagedObjects.
@interface VTDTodoItem : NSObject
@property (nonatomic, strong) NSDate* dueDate;
@property (nonatomic, copy) NSString* name;
+ (instancetype)todoItemWithDueDate:(NSDate*)dueDate name:(NSString*)name;
@end
Presenter
Presenter так же придерживается POSNO и содержит логику управления UI. Он знает когда показывать пользовательский интерфейс. Собирает входящие сигналы жестов пользователя и может обновлять UI, так же посылает реквесты Interactor’у. Presenter так же получает результаты из Interactor и преобразует результаты введенные из различных форм UI. Entities никогда не передаются из Interactor Presenter’у. Presenter может только подготавливать данные для отображения в UI.
- (void)addNewEntry
{
[self.listWireframe presentAddInterface];
}
- (void)foundUpcomingItems:(NSArray*)upcomingItems
{
if ([upcomingItems count] == 0)
{
[self.userInterface showNoContentMessage];
}
else
{
[self updateUserInterfaceWithUpcomingItems:upcomingItems];
}
}
View
View пассивен. Он ожидает пока Presenter ему не отдаст данные для отображения, при этом, никогда не просит его об этом. Методы, определенные во View должны позволять Presenter общаться с ним на уровне высокой абстракции. Presenter не знает об существовании таких элементов UI как UILabel, UIButton и так далее. Presenter знает только контенте и когда он должен быть отображен, а View определяет как его отобразить.
@protocol VTDAddViewInterface <NSObject>
- (void)setEntryName:(NSString *)name;
- (void)setEntryDueDate:(NSDate *)date;
@end
Routing
В VIPER ответственность Routing разделяется на два объекта: Presenter и wireframe. Wireframe - объект, который владеет UIWindow, UINavigationController, UIViewController и так далее. Он отвечает за создание View/ViewController и установку окна. Так как Presenter содержит логику на реакции пользователя, Presenter знает когда переходить на другой экран и с какого экрана. Между тем, wireframe знает как сделать эту навигацию. Значит, что Presenter будет использовать wireframe для выполнения навигаций.
@implementation VTDAddWireframe
- (void)presentAddInterfaceFromViewController:(UIViewController *)viewController
{
VTDAddViewController *addViewController = [self addViewController];
addViewController.eventHandler = self.addPresenter;
addViewController.modalPresentationStyle = UIModalPresentationCustom;
addViewController.transitioningDelegate = self;
[viewController presentViewController:addViewController animated:YES completion:nil];
self.presentedViewController = viewController;
}
#pragma mark - UIViewControllerTransitioningDelegate Methods
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
return [[VTDAddDismissalTransition alloc] init];
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source
{
return [[VTDAddPresentationTransition alloc] init];
}
@end
Подытожим
Надеюсь, я помог вам немного больше разобраться в этой архитектуре и почему бы не испробовать ее в следующем вашем приложении, которое вы собираетесь написать? Всем спасибо за внимание!