Что такое VIPER?

VIPER применяется в iOS приложениях, построенных на так называемой Clean Architecture. Мир VIPER основан на View, Interactor, Presenter, Entity, и Routing. Clean Architecture делит логику приложения на отдельные уровни ответственности, которые показаны ниже. image Рассмотрим главные части архитектуры подробнее:

View - Отображает то, что ему говорит Presenter и возвращает обратно данные тому же Presenter, введенные пользователем.

Interactor- Включает в себя всю бизнес-логику.

Presenter- Включает в себя View логику подготовки данных для отображения и реагирует на обратные действия пользователя.

Entity- Включает в себя базовую модель объектов, которые использует Interactor.

Routing- Отвечает за логику навигации, какие экраны показывать и в каком порядке.

Это разделение так же соответствует принципу “единоличная ответственность” или Single Responsibility Principle. То есть Interactor можно представить как бизнес-аналитика, Presenter взаимодействует с дизайнером, а View отвечает за визуального-дизайнера.

Ниже приведена схема, где показано как отдельные части архитектуры связаны между собой: image

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

Подытожим

Надеюсь, я помог вам немного больше разобраться в этой архитектуре и почему бы не испробовать ее в следующем вашем приложении, которое вы собираетесь написать? Всем спасибо за внимание!