In order to avoid a quarrel, advance statement: this article is pure translation, just for learning, plus limited level, forgive me!

[text] https://www.objc.io/issues/13-architecture/singletons/

Build the iOS app — by — with VIPERJeff Gilbert and Conrad Stoll

As we all know, in architecture, we shape our buildings, and our buildings shape us. As programmers eventually learn, this also applies to building software.

It is important to design our code so that each fragment is easily identifiable, has a specific and clear purpose, and fits in with the others in a reasonable way. This is what we call software architecture. Good architecture is not what makes a product successful, it is what makes it maintainable and helps maintainers maintain a clear mind.

In this article, we will introduce an iOS application architecture solution called VIPER. VIPER has been used to create many large projects, but for this article we show you the VIPER architecture by creating a to-do list application. You can follow this sample project on GitHub:

video

What is a VIPER?

Testing isn’t always a major part of building iOS applications. When we started looking to improve Mutual Mobile’s testing practices, we found it difficult to write test cases for iOS applications. We decided that if we were going to improve the way we test software, we’d first come up with a good way to build applications. We call this the VIPER.

For iOS applications, VIPER is the Architecture pattern for applying Clean Architecture. The word VIPER is a combination of the first letters of View, Interactor, Presenter, Entity and Routing. A clean architecture divides the application logic structure into different layers of responsibility. This makes dependency separation easier (e.g., your database) and the interaction between layer boundaries easy to test.

Most iOS applications use the MVC (Model-View-Controller) architecture. Using MVC as the architecture of your application makes you think of every class as a model as well as a view and controller. Because a lot of application logic is not part of models and views, it ends up in controllers. This leads to a problem called a Massive View Controller, where the View Controller does too much work. Slimming down a large view controller isn’t just a challenge for iOS programmers looking to improve the quality of their code, it’s also a great start to improving the architecture of the project.

VIPER’s different layers address this challenge by providing clear locations for application logic and navigation-related code. As you move through the VIPER architecture, you’ll realize that the view controllers in our to-do list example are lean and balanced, view control machines. You’ll also find that the code in view controllers and other classes is easier to understand and test, and therefore easier to maintain.

Application design based on use cases

Applications are typically implemented as a set of use cases. Use cases also become acceptance criteria or behaviors that describe what the application is for. Perhaps the list needs to be sorted by time, type, or name. That’s a use case. Use cases are the application layer responsible for business logic. Use cases should be independent of their user interface implementation. They should also be small and easy to define. Deciding how to break down a complex application into small use cases can be challenging and takes practice, but it’s useful to limit the scope of every problem you solve and every class you write.

Building an application with VIPER requires implementing a series of components to complete each use case. Application logic is a major part of every use case realization, but not the only part. Use cases also affect the user interface. In addition, it is important to consider how use cases work with other core components, such as networking and data presentation. Components are like plug-ins for use cases, and VIPER describes what each component and other roles are and how they interact with other components.

For our to-do list application, one of the use cases or requirements is to organize these to-do lists in different ways that the user chooses. By separating the logic that organizes the data into use cases, we can keep the user interface code clean and easily wrap the use cases in tests to ensure that it continues to work as expected.

The main part of VIPER

The main parts of VIPER are:

  • View (View) : Displays what the display tells it to display and sends user input back to the display.
  • Interaction (Interactor) : contains the business logic specified by the use case
  • Display (Presenter) : contains logic that prepares the content for presentation (when received from the interaction) and responds to the user’s input (by requesting new data from the interaction).
  • Entity (Entity) : contains the basic model objects used by the interaction.
  • Routing (Routing) : contains navigation logic that describes which interfaces and in which order.

These splits follow the principle of single liability. Interactors are responsible for business analysis, presenters for interaction design, and views for visual design.

Here is a diagram of the different components and how they are connected:

Interaction (Interactor)

An interaction represents a single application use case. It contains business logic that manipulates model objects (Entities) to perform specific tasks. The work done in the interaction should be independent of the UI. The same interaction can be used in iOS apps or OSX apps.

Because the interaction is a simple object (PONSO:Plain Old NSObject) that contains mostly logic, it is easy to develop using TDD.

The main use case for this simple application is to show the user’s upcoming purchases (for example, anything due next week). The business logic of this use case is to look up any to-do items that are due between today and the end of next week, and then assign them an associated due date: today, tomorrow, later this week, next week.

Here is the corresponding method from VTDListInteractor:

- (vodd)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]];
	}];
}
Copy the code

Entity (Entity)

Entities are model objects that are manipulated by interactors. Entities can only be manipulated by interactors. Interactors never pass entities to the presentation layer (e.g., presenters).

Entities are also common objects. If you’re using Core Data, you’re going to want to keep your managed objects behind the Data layer. Interactivators should not be used with NS-managed Objects.

Here is our to-do item entity:

@interface VTDTodoItem: NSObject
@property (nonatomic, strong) NSDate *dueDate;
@property (nonatomic, copy) NSString *name;
+ (instancetype)todoItemWithDueDate:(NSDate *)dueDate name:(NSString *)name;
@end
Copy the code

Don’t be surprised if your entities are just data structures. Any application-specific logic is mostly in the interactor.

Display (Presenter)

A demonstrator is a generic object that contains mostly the logic that drives the UI. It knows when to present the user interface. It takes input from the user interaction, so it can update the UI and send requests to the interactor.

AddNewEntry is called when the user clicks the ‘+’ button to add a new item. For this method, the presenter requires a wireframing UI for adding new items:

- (void)addNewEntry {
	[self.listWireframe presentAddInterface];
}
Copy the code

The presenter also receives the results from the interaction and converts them into a form that can be displayed in the view.

Here’s how to receive an upcoming project from the interaction. It processes the data and decides what to show the user:

- (void)foundUpcomingItems:(NSArray *)upcomingItems {
	if([upcomingItems count] == 0) {
		[self.userInterface showNoContentMessage];
	} else{ [self updateUserInterfaceWithUpcomingItems:upcomingItems]; }}Copy the code

Entities are never passed from the interactor to the presenter. Instead, simple, behaviorless data structures are passed from the interactor to the presenter. This prevents any “real work” from being done in the display. The presenter only prepares the data for display for the view.

View (View)

The view is passive. It waits for the display to show it; Never actively requests data from the display. Methods defined for a view (such as LoginView for the login interface) should allow the presenter to communicate with it at a high level of abstraction, presenting its content rather than how it is presented. The presenter is unaware of the existence of UILabel, UIButton, and so on. Only what it holds and when it should be displayed. How the content is presented depends on the view.

A view is an abstract interface defined for the Objective-C protocol. A view controller (UIViewController) or its subclasses will implement the view protocol. For example, the add interface in our example has the following interface:

@protocol VTDAddViewInterface <NSObject>
- (void)setEntryName:(NSString *)name;
- (void)setEntryDueDate:(NSDate *)date;
Copy the code

Both views and view controllers handle user interaction and input. It’s easy to understand why view controllers tend to get bloated, because this is where it’s easiest to process the input to perform some action. To keep the view controller streamlined, we need to provide a way to notify interested parts of the user when they perform certain actions. The view controller cannot make decisions based on these actions, but it can pass these events to where decisions can be made.

In our example, the “Add” view controller has event handler properties that match the following interface:

@protocol VTDAddModuleInterface <NSObject>
- (void)cancelAddAction;
- (void)saveAddActionWithName:(NSString *)name dueDate:(NSDate *)dueDate;
@end
Copy the code

When the user clicks the cancel button, the view controller tells the user’s specified event handler that it de-activates the add action. That way, the event handler can do the following: turn off the Add view controller and notify list view updates.

The boundary between the view and the presenter is a great place to use ReactiveCocoa. In this example, the view controller can provide methods that return signals representing button actions. This makes it easy for the presenter to respond to these signals without breaking the separation of responsibilities.

Routing (Routing)

Wireframes, designed by interaction designers, define the route from one interface to another. In VIPER, the responsibility for routing is taken care of by two objects, the display and the wireframes. Wireframe objects have UIWindow, UINavigationController, UIViewController, and so on. It’s responsible for threading the view/view controller and loading it onto the window.

Because the display contains logic that responds to user input, the display knows when to navigate to other interfaces and which interface to navigate to. Of course, wireframes also know how to navigate. Therefore, the display uses a wireframe diagram to perform navigation. Together, they describe a route to navigate from one view to the next.

Wireframes are also an obvious place to handle navigation transitions. Take a look at the example from “Add” wireframes:

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

#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];
}
Copy the code

The application uses a custom view controller transition to show the “Add” view controller. Because the wireframe is responsible for performing the transition action, it becomes the transition delegate for the “add” view controller and returns the appropriate transition animation.

Application components for VIPER

IOS app architecture needs to take into account the fact that UIKit and Cocoa Touch are the primary tools for building apps. The architecture needs to work with all the components in the application, but it also needs to provide reference guidelines for how and where some of the modules in the framework can be used.

The backbone of iOS apps is UIViewController. It’s easy to think that a competitor replacing MVC can avoid overuse of view controllers. But view controllers are at the heart of the platform: they handle screen flips, respond to user input, combine with system components like navigation controllers, and now, in iOS7, perhaps custom interface transitions. Very useful.

With VIPER, the view controller does what it’s supposed to do: control the view. Our to-do list application has two view controllers, one for the list screen and the other for the Add screen. The implementation of the “Add” view controller is very basic, because all it does is control the view:

@implementation VTDAddViewController - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismiss)]; [the self transitioningBackgroundView addGestureRecognizer: gestureRecognizer]; self.transitioningBackgroundView.userInteractionEnabled = YES; } - (void)dismiss { [self.eventHandler cancelAddAction]; } - (void)setEntryName:(NSString *)name {
	self.nameTextField.text = name;
}

- (void)setEntryDueDate:(NSDate *)date {
	[self.datePicker setDate:date];
}
- (IBAction)save:(id)sender {
	[self.eventHandler saveAddActionWithName:self.nameTextField.text dueDate:self.datePicker.date];
}
- (IBAction)cancel:(id)sender {
	[self.eventHandler cancelAddAction];
}
#pragma mark - UITextFieldDelegate Methods
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
	[textField resignFirstResponder];
	return YES;
}
@end
Copy the code

Applications are often more attractive when they are connected to the web. But where should networking take place? Who should start it? Typically, it is up to the interactor to initiate network operations, but it does not directly deal with the networking code. It will request a dependency like a network manager or API client. The interactor may need to aggregate data from multiple sources to provide the information needed to complete the use case. The data returned by the interactor is then received by the presenter and formatted for the presentation.

The data store is responsible for providing entities to the interactor. As the interactor applies its interaction logic, it needs to fetch the entity from the data store, process the entity, and put the updated entity back into the data store. Data stores manage persistent entities. Entities do not know about data stores and therefore do not know how to persist themselves.

The interaction should also not know how to persist entities. Sometimes an interactor may need to use an object called a data manager to help it interact with the data store. The data manager handles operations of specific storage types, such as creating fetch data requests, creating queries, and so on. This allows the interactor to focus more on the application logic without knowing how entities are acquired and persisted. It makes sense to use the Data manager when you’re working with Core Data, which you can see described below.

This is the data manager interface that the sample should have:

@interface VTDListDataManager: NSObject
@property (nonatomic, strong) VTDCoreDataStore *dataStore;
- (void)todoItemsBetweenStartDate:(NSData *)startDate endDate:(NSDate *)endDate completionBlock:(void (^)(NSArray *todoItems))completionBlock;
@end
Copy the code

But when developing an interactor with TDD, you can switch production data stores using a test double/mock. Not communicating with remote servers (for Web services) and local disks (for databases) makes your tests faster and more repeatable.

The reason for storing data in a well-defined layer is that it allows you to defer the choice of a particular persistence technique. If your data store is a single class, you can start your application using a basic persistence strategy and then upgrade to SQLite or Core Date if appropriate without changing anything else in the application code base.

The use of Core Date in iOS projects often causes more controversy than the architecture itself. However, using Core Date in VIPER can be one of the best Core Date experiences you’ve ever had. Core Date is a great data persistence tool with extremely fast fetch times and very low memory footprint. But there’s a convention that you have to set up a cumbersome NS-managed Object context in your application’s implementation file, even though it shouldn’t be there. VIPER puts Core Data where it belongs: the Data storage tier.

In the to-do list example, the only two parts of the application that know that Core Data is being used are the Data store itself, which is where the Core Data stack and Data manager are set up. The data manager performs the fetch request, converts the NS-Managed Dobjects object returned by the data store layer into a standard simple object model, and returns it to the business logic layer. That way, the Core of your application doesn’t depend on Core Data, and in return, you don’t have to worry about your application not working due to outdated or threadily nS-managed Objects.

In the Data manager, when a request is made to access the Core Data store, it looks like this:

@implementatin VTDListDataManager
- (void)todoItemsBetweenStartDate:(NSDate *)startDate endDate:(NSDate *)endDate completionBlock:(void (^)(NSArray *todoItems))completionBlock {
	NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
	NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(date >= %@) AND (date <= %@)", [calendar dateForBeginningOfDay:startDate], [calendar dateForEndOfDay:endDate]];
	NSArray *sortDescriptors = @[];
	__weak type(self) welf = self;
	[self.dateStore fetchEntriesWithPredicate:predicate sortDescriptors:sortDescriptors completionBlock:^(NSArray *entries){
		if(completionBlock) {
			completionBlock([welf todoItemsFromDataStoreEntries:entries]);
		}
	}];
}

- (NSArray *)todoItemsFromDataStoreEntries:(NSArray *)entries {
	return [entries arrayFromObjectsCollectedWithBlock:^id(VTDManagedTodoItems *todo) {
		return [VTDTodoItem todoItemWithDueDate:todo.date name:todo.name];
	}];
}
@end
Copy the code

Almost as controversial as Core Data is UI Storyboards. Storyboards have many features to use, and it would be a mistake to ignore them entirely. However, it is difficult to achieve all of VIPER’s goals when using all of the features storyboard offers.

Often, the compromise we make is to choose not to use segues (connections between controllers in storyboards). There may be cases where it makes sense to use wires, and the danger with Segues is that it is difficult to maintain a complete separation between interfaces and between UI and application logic. In general, we try not to use segues when it is obvious to implement the prepareForSegue method.

In addition, Storyboards are a great way to implement user interface Layout, especially when using Auto Layout. We implemented both of the interfaces in the to-do list example in storyboard, and then performed our own navigation with the following code:

static NSString *ListViewControllerIdentifier = @"VTDListViewController";
@implementation VTDListWireframe
- (void)presentListInterfaceFromWindow:(UIWindow *)window {
	VTDListViewController *listViewController = [self listViewControllerFromStoryboard];
	listViewController.eventHandler = self.listPresenter;
	self.listPresenter.userInterface = listViewController;
	self.listViewController = listViewController;
	[self.rootWireframe showRootViewController:listViewController inWindow:window];
}
- (VTDListViewController *)listViewControllerFromStoryboard {
	UIStoryboard *storyboard = [self mainStoryboard];
	VTDListViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:ListViewControllerIdentifier];
	return viewController;
}
- (UIStoryboard *)mainStoryboard {
	UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
	return storyboard;
}
@end
Copy the code

Build modules using VIPER

Often with VIPER, you will find that an interface or set of interfaces will be organized together as a module. A module can be described in several ways, and it is usually best to describe it as a feature. In a podcast application, the module might be an audio player or a subscription browser. In our to-do list application, the list and the Add interface are built as separate modules.

Designing your application as a series of modules has several benefits. One is that a module has a clear and well-defined interface and is independent of other modules. This allows you to add/remove features or change the way your interface presents various modules to the user.

We wanted to distinguish modules clearly in the to-do list example, so we defined two protocols for the “add” module. The first is the module interface, which defines what a module can do. The second is the module delegate, which describes what the module does. Such as:

@protocol VTDAddModuleInterface <NSObject> 
- (void)cancelAddAction;
- (void)saveAddActionWithName:(NSString *)name dueDate:(NSDate *)dueDate;
@end

@protocol VTDAddModuleDelegate <NSObject>
- (void)addModuleDidCancelAddAction;
- (void)addModelDidSaveAddAction;
@end
Copy the code

Because modules must be presented to the user, modules usually implement module interfaces. When another interface wants to present this module, its presenter needs to implement the module interface protocol so that it knows what the module does when it presents it.

A module may contain a common layer of application logic for entities, interactors, and managers for multiple interfaces. Of course, this depends on how these interfaces interact with each other and how similar they are to each other. A module can easily represent an interface, as shown in the to-do list example. In this case, the application logic layer can correspond to very specific behavior in a particular module.

Modules are also a good way to organize code. Hiding a module’s code in its own folder and organizing it in Xcode makes it easy to find what you need to change. It’s a great feeling when you find a class where you expect it to be.

Another benefit of using VIPER building blocks is that they can be easily extended to many forms. Separating application logic from use cases at the interaction layer lets you reuse the application layer while still focusing on building new user interfaces for tablets, phones, and Macs.

Furthermore, the user interface of an iPad app may reuse some of the views, view controllers, and displays of an iPhone app. In this case, an iPad interface might be represented by a parent display and wireframes, and it might use an existing iPhone display and wireframes to compose the interface. Building and maintaining cross-platform applications can be quite challenging, but a good architecture that promotes reuse throughout the application and application layer can make this easier.

Use VIPER for testing

VIPER encourages separation of concerns, which makes it easier to adapt to TDD. The interaction contains pure logic independent of the UI, which makes testing easier to drive. The presenter contains the logic to prepare the data for presentation and is independent of any UIKit control. Developing this logic also makes testing easier to drive.

Our preferred approach is to start with the interactor. Everything in the UI fits the needs of the use case. By using THE TDD API for the interaction to test drivers, you will better understand the relationship between the UI and use cases.

For example, we’ll see the interactor responsible for the upcoming to-do list. The rule for finding upcoming items is to look up all items as of the end of next week and categorize each item as of today, tomorrow, later in the week, or next week.

The first example we wrote ensures that the interactionator finds all the to-do items by the end of next week:

- (void)testFindingUpcomingItemsRequestsAllToDoItemsFromTodayThroughEndOfNextWeek {
	[[self.dataManager expect] todoItemsBetweenStartDate:self.today endDate:self.endOfNextWeek completionBlock:OCMOCK_ANY];
	[self.interactor findUpcomingItems];
}
Copy the code

Once we know that the interaction requests the appropriate backlog, we’ll write several tests to make sure it assigns the backlog to the correct set of relevant dates (for example: today, tomorrow, etc.).

- (void)testFindingUpcomingItemsWithOneItemDueTodayReturnsOneUpcomingItemsForToday {
	NSArray *todoItems = @[[VTDTodoItem todoItemWithDueDate:self.today name:@"Item 1"]]. [self dataStoreWillReturnToDoItems:todoItems]; NSArray *upcomingItems = @[[VTDUpcomingItem upcomingItemWithDateRelation:VTDNearTermDateRelationToday dueDate:self.today  title:@"Item 1"]];
	[self expectUpcomingItems:upcomingItems];
	
	[self.interactor findUpcomingItems];
}
Copy the code

Now that we know what the interactive API looks like, we can develop a demonstrator. When the presenter receives an upcoming to-do from the interaction, we will test that we have formatted the data correctly and displayed it on the UI:

- (void)testFoundZeroUpcomingItemsDisplaysNoContentMessage {
	[[self.ui expect] showNoContentMessage];
	[self.presenter foundUpcomingItems:@[]];
}
- (void)testFoundUpcomingItemForTodayDisplaysUpcomingDataWithNoDay {
	VTDUpcomingDisplayData *displayData = [self displayDataWithSectionName:@"Today" sectionImageName:@"check" itemTitle:@"Get a haircut" itemDueDay:@""];
	[[self.ui expect] showUpcomingDisplayData:displayData];
	NSCalendar *calendar = [NSCalendar gregorianCalendar];
	NSData *dueData = [calendar dateWithYear:2014 month:5 day:29];
	VTDUpcomingItem *haircut = [VTDUpcomingItem upcomingItemWithDateRelation:VTDNearTermDateRelationToday dueDate:dueDate title:@"Get a haircut"];
	[self.presenter foundUpcomingItems:@"haircut"];
}
- (void)testFoundUpcomingItemForTomorrowDisplaysUpcomingDataWithDay {
	VTDUpcomingDisplayData *displayData = [self displayDataWithSectionName:@"Tomorrow" sectionImageName:@"alarm" itemTitle:@"Buy groceries" itemDueDay:@"Thursday"];
	[[self.ui expect] showUpcomingDisplayData:displayData];
	NSCalendar *calendar = [NSCalendar gregorianCalendar];
	NSDate *dueDate = [calendar dateWithYear:2014 month:5 day:29]; 
	VTDUpcomingItem *groceries = [VTDUpcomingItem upcomingItemWithDateRelation:VTDNearTermDateRelationTomorrow dueDate:dueDate title:@"Buy groceries"];
	[self.presenter foundUpcomingItems:@[groceries]];
}
Copy the code

We also want to test that when the user wants to add a new backlog, the application will start doing the appropriate thing:

- (void)testAddNewToDoItemActionPresentsAddToDoUI {
	[[self.wireframe expect] presentAddInterface];
	[self.presenter addNewEntry];
}
Copy the code

Now we can develop the view. We display a special message when there is no upcoming to-do list:

- (void)testShowingNoContentMessageShowsNoContentView {
	[self.view showNoContentMessage];
	XCTAssertEqualObjects(self.view.view, self.view.noContentView, @"the no content view should be the view");
}
Copy the code

When there is an upcoming to-do list presentation, I want to make sure the list is displayed:

- (void)testShowingUpcomingItemsShowsTableView {
	[self.view showUpcomingDisplayData:nil];
	XCTAssertEqualObjects(self.view.view, self.view.tableView, @"the table view should be the view");
}
Copy the code

Building an interaction is a natural fit with TDD first. If you develop the interaction first and then the presentation, you will build a set of test methods around these layers to lay the foundation for implementing these use cases. You can walk through these classes quickly because you don’t need to interact with the UI to test them. Then when you start developing the view, you have a workable, tested logic and a presentation layer connected to it. By the time you’re done developing your view, you’ll probably find that everything works fine when you first run your application, because all the tests you passed tell you it works.

conclusion

I hope you enjoyed this introduction to VIPER. Now, many of you are probably wondering what to do next. If you want to build your next application with VIPER, where do you start?

This article and the example applications implemented using VIPER are as specific and well defined as we can be. Our to-do list application is fairly simple, but it’s also a pretty accurate illustration of how to build an application using VIPER. Whether or not you strictly follow the example in a real project depends on your own set of challenges and constraints. In our experience, each of our projects slightly changed the way VIPER was used, but they all benefited from the way they were guided.

For a variety of reasons, you may deviate from the path set by VIPER. Maybe you’ll encounter a “rabbit” object, or your application will benefit from using segues in Storyboard. That’s okay. In those cases, when you’re making a decision, think about what VIPER stands for. At the heart of VIPER is an architecture based on the principle of single responsibility. Think about this principle if you have any questions when deciding what to do next.

You may be wondering if it’s possible to use VIPER in an existing application. In this case, consider building a new feature using VIEPR. Many of our existing projects can take this approach. This allows you to build a module using VIPER and can help you identify any existing problems that make it difficult to adapt to an architecture based on the principle of single responsibility.

Every app is different this is one of the most important things about developing software, and there are different ways to build apps. For us, this means that every app is a new opportunity to learn and try something new.

Swift addendum

At apple’s Developer conference last week, Apple introduced Swift as the programming language for developing Cocoa and Cocoa Touch in the future. It’s too early to comment on Swift in depth, but we do know that the language has had a major impact on how software is designed and built. We decided to rewrite our VIPER backlog sample application using Swift to help us see what this means for VIPER. So far, we like what we see. Here are a few Swift features that I think can improve the experience of building apps with VIPER.

Structs

In VIPER we use small and lightweight model classes to pass data between layers, such as from the display to the view. These ordinary objects are usually intended to simply carry a small amount of data and do not want to be subclassed. The Swift architecture fits perfectly into these situations. Here is an example of using structure in the VIPER Swift example. Note that this construct requires an equality operation, so we override the “==” operator to compare two instances of the same type:

struct UpcomingDisplayItem: Equatable, Printable {
	let title: String = ""
	let dueDate: String = ""
	var description: String {
		get {
			return "\(title) -- \(dueDate)"
		}
	}
	init(title: String, dueDate: String) {
		self.title = title
		self.dueDate = dueDate
	}
}
func ==(leftSide: UpcomingDisplayItem, rightSide: UpcomingDisplayItem) -> Bool {
	var hasEqualSections = false
	hasEqualSections = rightSide.title == leftSide.title
	if hasEqualSections == false {
		return false
	}
	hasEqualSections = rightSide.dueDate == rightSide.dueDate
	return haseEqualSections
}
Copy the code

Type safety

Perhaps the biggest difference between Object-C and Swift is the handling of types. Object-c is dynamically typed and Swift is very strict about the way it implements type checking at compile time. For an architecture like VIPER, which consists of many different layers, type safety is a huge win for programmer efficiency and overall architecture. The compiler helps you ensure that containers and objects are typed correctly as they pass between layer boundaries. As shown above, this is a good place to use structure. If a structure wants to survive between the two layers, thanks to type safety, you can guarantee that it will never be able to escape between the two layers.