SkillAgentSearch skills...

ZIKViper

iOS VIPER implementation, demonstrating discovering modules and injecting dependencies with protocol. Provide VIPER code Templates.

Install / Use

/learn @Zuikyo/ZIKViper
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

ZIKViper

iOS VIPER implementation, demonstrating discovering modules and injecting dependencies with protocol. Provide VIPER code Templates.

You can get answers about these discussions here:

  • How to decouple modules thoroughly
  • How to import a sub VIPER module
  • How a sub module communicates with it's parent module
  • How to inject dependencies to a module
  • Interface-oriented router

两个iOS VIPER架构实现,关注于模块化和基于接口的依赖注入,并且提供快速生成VIPER代码的模板。

具体展示了以下问题的解决方案:

  • 如何彻底地解决不同模块之间的耦合
  • 如何在一个模块里引入子模块
  • 子模块和父模块之间如何通信
  • 如何对模块进行依赖注入
  • 面向接口的路由工具

中文文档

Get Started

This repository uses submodule ZIKRouter, so you need to clone the submodule:

git clone --recursive https://github.com/Zuikyo/ZIKViper.git

Catalog

<a name="VIPER-intro"></a>What's VIPER

The word VIPER is a backronym for:

  • View
  • Interactor
  • Presenter
  • Entity
  • Router

A diagram for VIPER:

VIPER

(from iOS Architecture Patterns: Demystifying MVC, MVP, MVVM and VIPER)

If it's new for you, check out these articles to learn VIPER architecture:

<a name="Clean-Architecture"></a>Clean Architecture

Mutual mobile first came up with VIPER in 2013. VIPER is an application of Clean Architecture, so you have to know Clean Architecture before practicing VIPER.

Clean Architecture divides an app’s logical structure into distinct layers of responsibility. This makes it easier to isolate dependencies and to test the interactions at the boundaries between layers.

The diagram for Clean Architecture:

Clean Architecture

The key is the dependency rule:

The Dependency Rule

The concentric circles represent different areas of software. In general, the further in you go, the higher level the software becomes. The outer circles are mechanisms. The inner circles are policies.

The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in an inner circle. That includes, functions, classes. variables, or any other named software entity.

Those components in VIPER conform to this dependency rule.

<a name="implementation1"></a>First Implementation: Thorough VIPER

I will give two implementations of VIPER here. The first is a thoroughly decoupled implementation. Then I will show another implementation, an easier one allowing some coupling.

The thorough implementation describes dependency injection, module communication, module decoupling. I add some new components.

Below is the diagram. The color of the component is corresponding to their color in the diagram of Clean Architecture:

thorough viper

I will use a note app as sample.

<a name="implementation1-view"></a>View

View can be a UIView + UIViewController, or a custom UIView, or a Manager of UIView, as long as it implements the interface of View.

Responsibilities:

  • Assemble different view widgets, and manage their layouts
  • Provide view input interface to presenter for updating view
  • Send view events to eventHandler (the presenter)
  • Get data for displaying views from viewDataSource (the presenter)
  • Provide routeSource to presenter as the source view controller for navigation

View will import many widgets and implements their delegates. View will separate methods in these delegates into ViewEventHandlerInput and ViewDataSourceInput for different responsibilities.

viewDataSource and viewEventHandler stand for the two responsibilities of presenter for View.

Sample code:

@protocol ZIKNoteListViewEventHandler <NSObject>
- (void)handleDidSelectRowAtIndexPath:(NSIndexPath *)indexPath;
@end
@protocol ZIKNoteListViewDataSource <NSObject>
- (NSInteger)numberOfRowsInSection:(NSInteger)section;
- (NSString *)textOfCellForRowAtIndexPath:(NSIndexPath *)indexPath;
- (NSString *)detailTextOfCellForRowAtIndexPath:(NSIndexPath *)indexPath;
@end
@interface ZIKNoteListViewController () <UITableViewDelegate,UITableViewDataSource>
@property (nonatomic, strong) id<ZIKNoteListViewEventHandler> eventHandler;
@property (nonatomic, strong) id<ZIKNoteListViewDataSource> viewDataSource;
@property (weak, nonatomic) IBOutlet UITableView *noteListTableView;
@end

@implementation ZIKNoteListViewController

- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
                                      text:(NSString *)text
                                detailText:(NSString *)detailText {
    UITableViewCell *cell = [self.noteListTableView dequeueReusableCellWithIdentifier:@"noteListCell" forIndexPath:indexPath];
    cell.textLabel.text = text;
    cell.detailTextLabel.text = detailText;
    return cell;
}


#pragma mark UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.viewDataSource numberOfRowsInSection:section];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *text = [self.viewDataSource textOfCellForRowAtIndexPath:indexPath];
    NSString *detailText = [self.viewDataSource detailTextOfCellForRowAtIndexPath:indexPath];
    UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath
                                                   text:text
                                             detailText:detailText];
    return cell;
}

#pragma mark UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    [self.eventHandler handleDidSelectRowAtIndexPath:indexPath];
}

@end

<a name="implementation1-presenter"></a>Presenter

Responsibilities:

  • Handle view event, interpreting to wireframe or interactor
  • Provide view dataSource from interactor to view
  • Update view by interfaces in ViewInput
  • Maintain states of view
  • Call use cases provided by interactor
  • Handle interactor event
  • Provide datas of view from view to interactor
  • Show other view modules by wireframe

Presenter is a transfer station between view and interactor. It transforms different events to business logic. You shouldn't import UIKit in presenter to affect rendering of view directly.

Sample code:

@interface ZIKNoteListViewPresenter ()<ZIKNoteListViewDataSource,ZIKNoteListViewEventHandler>
@property (nonatomic, strong) id<ZIKNoteListWireframeProtocol> wireframe;
@property (nonatomic, weak) id<ZIKViperView,ZIKNoteListViewProtocol> view;
@property (nonatomic, strong) id<ZIKNoteListInteractorInput> interactor;
@end

@implementation ZIKNoteListViewPresenter

#pragma mark ZIKNoteListViewDataSource

- (NSInteger)numberOfRowsInSection:(NSInteger)section {
    return self.interactor.noteCount;
}

- (NSString *)textOfCellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *title = [self.interactor titleForNoteAtIndex:indexPath.row];
    return title;
}

- (NSString *)detailTextOfCellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *content = [self.interactor contentForNoteAtIndex:indexPath.row];
    return content;
}

#pragma mark ZIKNoteListViewEventHandler

- (void)handleDidSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *uuid = [self.interactor noteUUIDAtIndex:indexPath.row];
    NSString *title = [self.interactor noteTitleAtIndex:indexPath.row];
    NSString *content = [self.interactor noteContentAtIndex:indexPath.row];
    
    [self.wireframe pushEditorViewForEditingNoteWithUUID:uuid title:title content:content delegate:self];
}

@end

<a name

Related Skills

View on GitHub
GitHub Stars269
CategoryDevelopment
Updated1mo ago
Forks52

Languages

Objective-C

Security Score

100/100

Audited on Feb 8, 2026

No findings