"Editor's note" This article was written by Jeff Gilbert and Conrad Stoll, to build a basic sample application, gain insight into VIPER, and clarify the overall layout and ideas of VIPER from multiple components such as views, interactivity, and more. Build IOS application architectures through VIPER, improve application quality, and meet new opportunities for application construction! This article is compiled and organized by ONEAPM engineers
As we all know, in the field of architecture, we shape our own buildings, and buildings in turn affect us. For programmers, the same applies to building software.
In programming, it is important to make the code readable, and in addition to the code, it has a clear purpose and can be logically aligned with other code. This is what we often call the software architecture. A good architecture does not guarantee a successful product, but it makes the product easier to maintain, and does not make the person you read mad.
In this article, the author describes a method that is applicable in IOS apps, named VIPER. VIPER has been used to build many large projects, but the purpose of this article is to gain an in-depth understanding of VIPER by building a backlog of applications. You can find the sample project on GitHub.
What is Viper?
Testing is not always an important part of developing IOS apps. When the authors sought ways to improve testing practices at Mutual Mobile, it was not easy to write tests for IOS applications. The author realizes that if you want to find a way to improve your test software, you first need to think of a better application architecture. So the better way is called VIPER.
VIPER is a neat architecture for IOS programs. It refers to View, Interactor, Presenter, Entity, and Routing. A neat architecture assigns the logic of the application to different areas of responsibility. This makes dependencies, such as databases, easier to stand-alone and more convenient for testing the interaction between layers and layers.
Most IOS apps are using the MVC (model-View-controller) architecture. Using MVC as a program's architecture can lead you to think about each class as a model, view, or controller. Because most application logic is not part of a model or view, it usually ends in the controller. This led to the so-called large-scale view controller, whose view controller eventually became cumbersome. Offloading these huge view controllers is an issue that iOS developers must face and a huge challenge to improve the quality of their code.
By locating program logic and navigating related code, different layers of VIPER can help solve this problem. With VIPER's application, the to-do list instance, you will find that the view controller becomes tiny and symmetrical. The Code of the view controller and all classes are easy to understand and test, and more conducive to post-maintenance.
Application design based on use case
Applications are often implemented as a set of use cases. A use case is called acceptance criteria or behavior, and it also describes the purpose of the program. A list might need to be sorted by date, type, or name, which is a use case. The use case is the logical responsibility layer of the program, should be independent of the user interface implementation, they should be small and clear. Deciding how to break up a complex program into smaller use cases is challenging and requires accumulating practical experience. But it can effectively limit the scope of each problem and class.
Building an application with VIPER requires implementing a series of components to satisfy each use case. Application logic is an important and not unique part of implementing a use case. Use cases can also affect the user interface. In addition, you need to consider how use cases combine core components such as network and data persistence. In use cases, the build is like a plug-in, VIPER is used to describe the functionality of the components and how they interact with each other.
One use case or requirement for a to-do application is to group the items based on user selection. By separating the logic of converting data into a use case, we are able to maintain the cleanliness of the user interface code and facilitate the packaging of use cases in testing to ensure that it continues to work in the normal way.
Main components of the VIPER
The main components of VIPER have the following parts:
- View: Displays the requirements of the viewer and returns user input.
- Interactivity: Contains the business logic specified by the use case.
- Display: Contains the view logic used to prepare the display (received from the interactive) and feedback user input (request up-to-date data through the monitor).
- Entity: Contains the base model object used by the interaction.
- Routing: Contains navigation logic to describe the order in which the screen appears.
This separation is also in line with the principle of single responsibility. As a business analyst, the visualizer becomes an interactive designer and the view is responsible for visualizing the design.
Here are the different components and how they relate to each other:
While VIPER components can be implemented in any order in the application, we choose to introduce the components in the recommended implementation order. You will find that this sequence is basically consistent with the application's build process, first of all to discuss what the application product needs to do, and then how the user interacts with it.
Interactive device
An interaction represents a use case in an application that contains business logic to manipulate model objects (entities) for specific tasks. The work done by the interaction should be independent of any user interface. The same interaction can be used in IOS apps or OS X apps.
Because the interaction is a ponso (normal old-fashioned nsobject), it mainly contains logic, it is easy to use TDD to develop.
The primary use case for the sample program is to display the user's next to-do item (that is, the task until the end of next week). The business logic of this use case is to look for a backlog between today and next weekend, assigned to a relative due date: Today, tomorrow, after the week, or next week.
Here's a similar approach to Vtdlistinteractor:
- (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
An entity is a model object manipulated by the interaction (which is manipulated only by the interaction), and the interaction does not pass the entity to the presentation layer (that is, the viewer).
Entities are often also ponsos. If you are using core data, you will want your management objects to remain at the back end of the data layer. The nsmanagedobjects cannot be used directly by the interaction.
Here is the entity for the sample app:
@interface VTDTodoItem : NSObject@property (nonatomic, strong) NSDate* dueDate;@property (nonatomic, copy) NSString* name;+ (instancetype)todoItemWithDueDate:(NSDate*)dueDate name:(NSString*)name;@end
Don't be surprised if your entity is just a data structure, and any logic that relies on the program should be in the interaction.
Display device
The display is a ponso, consisting mainly of logic to drive the user interface. It knows when to render the user interface, collects input from the user interaction process, updates the UI in real time, and sends a response request like an interactive device.
Addnewentry is called when the user clicks the +
button to add a new to-do item. In response to the action, the display call wireframe to display the UI to add a new item:
- (void)addNewEntry{ [self.listWireframe presentAddInterface];}
The viewer can also display the results of the interaction and convert the results into other forms that can be effectively displayed in the view.
The following is the method that is called after the display is received from the viewer to the TODO item. It will process the relevant data and determine which content will be presented to the user:
- (void)foundUpcomingItems:(NSArray*)upcomingItems{ if ([upcomingItems count] == 0) { [self.userInterface showNoContentMessage]; } else { [self updateUserInterfaceWithUpcomingItems:upcomingItems]; }}
Entities are never transferred from the interaction to the viewer. Conversely, simple data structures that are not behaving can be transmitted. This prevents any "actual work" from being performed in the display. The display is only responsible for preparing the data in the view display.
View
The view is usually passive. It shows what the display is transmitting, but does not actively request data from the viewer. The method defined for a view (for example, LoginView requires a login interface), should allow the viewer to communicate at a higher level of abstraction, and the display will display its content directly, without caring about how the content is to be displayed. The display does not know UILabel, UIButton and other controls, only know how to maintain content and display timing. How the content is to be presented depends entirely on the view.
The view is an abstract interface, and the applicable protocol is defined in OBJECTIVE-C. A uiviewcontroller or its subclasses will implement the View protocol. For example, the "Add" interface in this example has the following interface:
@protocol VTDAddViewInterface <NSObject>- (void)setEntryName:(NSString *)name;- (void)setEntryDueDate:(NSDate *)date;@end
The view and view controller also handles user interaction and user input. So it's not hard to understand why view controllers are often large because they are the easiest to handle user input and perform related actions. To keep the view controllers tilted, they need to be notified of the effective way to inform the parties after certain actions have been taken by the user. The view controller does not respond to user actions, only passing events to the response method.
In this example, add the event handling properties of the view controller with the following interface:
@protocol VTDAddModuleInterface <NSObject>- (void)cancelAddAction;- (void)saveAddActionWithName:(NSString *)name dueDate:(NSDate *)dueDate@end
When the user clicks the Cancel button, the view controller informs the event-handling mechanism that the user needs to cancel the add operation. This enables the event handling mechanism to cancel the addition of the view controller and inform the list view to update.
The boundary between the view and the display can be used for Reactivecocoa. In this example, the view controller can also provide a method to return a signal that represents a button action. This will allow the display to make it easier to give feedback to the signal without disrupting the independence of the responsible area.
Routing
The routing between interfaces is defined in the wireframe created by the interaction designer. In VIPER, the task of routing is to implement sharing between the display and the wireframe. Wireframe objects include Theuiwindow, Uinavigationcontroller, and Uiviewcontroller, which are responsible for creating the view/View controller and completing the assembly in the window.
Because the display contains logic that responds to user input, it knows when to navigate to another screen, which interface should be navigated to, and how the wireframe knows how to navigate. The display mainly uses the wireframe to realize the navigation function. Wireframe and display work together to describe the process of a screen to the next route.
Wireframe facilitates the handling of navigation transition animations. Take a look at the example of adding a wireframe below:
@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 *) Prese Nting Sourcecontroller: (Uiviewcontroller *) source {return [[Vtdaddpresentat Iontransition alloc] init];} @end
The application uses a custom view controller transition to add a view controller. Because the wireframe is responsible for performing the transition, it becomes a transition delegate that adds a view controller and can return the appropriate transition animation.
Organizing application components with VIPER
The architecture of the IOS app needs to be understood, as the main development tool, the role of UIKit and COCAA Touch is to build the "façade" of the application. The architecture needs to coexist peacefully with all components of the application, but it also needs to provide recommendations for the use of some of the frameworks and where they are located.
The main force of IOS apps is Uiviewcontroller, which is often considered a competitor to replace MVC and can be used to reduce the use of view controllers. But view controllers are central to the platform: they handle directional changes, respond to user input, and integrate system components such as navigation controllers. (not to be continued ...) )
Stay tuned: Build IOS App architecture with VIPER series (2).
Original address: architecting IOS Apps with VIPER
This article is compiled and collated by OneAPM engineers. OneAPM is an emerging leader in application performance management, enabling enterprise users and developers to easily implement slow program code and real-time crawling of SQL statements. To read more technical articles, please visit the OneAPM official blog.
Building IOS Application Architectures with VIPER (1)