VIPER

master

A lightweight VIPER software architecture framework for Swift.
thomverbeek/VIPER

VIPER

Swift Platform Releases Swift Package Manager

VIPER is a lightweight software architecture framework for Swift.

Clean Architecture is a software architecture pattern devised by Robert C. Martin in 2012 that promotes the SOLID principles of software design. The concept of VIPER is an iOS architecture pattern inspired by the Clean Architecture, originally coined by developers of Mutual Mobile and popularised by their objc.io article. This framework is a Swift implementation of the aforementioned architecture principles that enables you to build VIPER apps for iOS, macOS and tvOS.

Installation

In true Swift fashion, VIPER is only available as a Swift Package.

Swift Package Manager

Add the following to your Package.swift's dependencies: array:

.package(url: "git@github.com:thomverbeek/VIPER.git", from: "0.5.1"),

Xcode

In your project or workspace, choose File ▸ Swift Packages ▸ Add Package Dependency… to add https://github.com/thomverbeek/VIPER as a package dependency.

VIPER Command Line Tools

This package comes bundled with viper-tools, a command line utility.

  • Open up Terminal, cd into this Swift package, and run the following command:
$ swift run viper-tools
  • This tool is configurable with subcommands. Use the generate subcommand to generate a new VIPER module. For example, to generate a module called "MyModule" for macOS on the Desktop:
$ swift run viper-tools generate MyModule ~/Desktop/ --os macOS --verbose
  • You can use the --exclude-directory flag to generate files without a directory. This is very useful when grouping your VIPER modules into Swift Packages in your project.

About VIPER

VIPER divides application logic into distinct components of responsibility:

  • View: UI logic, including any user interaction;
  • Interactor: business logic, akin to application use cases;
  • Presenter: presentation logic, which maps business logic to view logic;
  • Entity: entity logic, maintained by repositories & services;
  • Router: navigation logic, which lives in the same realm as the view.

Conceptually, these five components form a collective Module, synonymous with a single screen in your iOS application. The lifecycle of each module is visually represented by the View, which indirectly holds reference to all components. These components communicate with one another in an orchestrated order, and once the View dismisses, the lifecycle ends. The resulting code is clear, testable, modular and scalable with large teams.

The VIPER manifesto isn't without its flaws:

  • The Router's role wasn't clearly defined, making it difficult to implement. VIPER intended to solve the Assembler Problem by enabling the Router to assemble VIPER modules. But this arrangement jeopardises the Single Responsibility Principle as it already takes responsibility for navigation. And speaking of navigation, the Router's task to pass information between VIPER modules was also left in the dark.
  • The Entity component was likely a catch-all term for "anything else" in the VIPER backronym. It lacked a clear definition, hinting at an entity layer exclusively for the Interactor to interact with. An ethereal interpretation sees simple entities forming the basis of all message passing between the various layers of the VIPER module. In practice, it's likely that Entity encompasses any repositories or services an Interactor may engage with. Without clarity on where these repositories or services come from, the VIPER definition likely needs to include dependency injection.
  • VIPER stipulates bi-directional data flow between components, without specifying which component holds state. As the iOS landscape makes its transition towards functional reactive programming, it's important to clearly define the source of state and minimise the potential paths through which this data (and semantic errors) can travel.

There are numerous implementations out in the wild that try to meet these requirements and then some, but they leave a bit more to be desired. VIPER can be difficult to grasp and fully implement as a framework, and the Swift language throws even more hurdles in the mix due to its linguistic quirks and type-safe limitation. Most frameworks out there simply attempt to translate the original Objective-C sample code to Swift, forcing the developer to wire up components manually and force-cast between types. These frustrations ultimately led to the development of this framework to bring VIPER to the masses.

“Simplicity is the ultimate sophistication”

This framework leverages a combination of generics, static scopes and functional reactive programming principles to distill VIPER down to a single file of under 300 lines of code, including comments. Check out VIPER.swift.

  • It allows the compiler to help guide beginners, yet provides swiss-army flexibility to advancers.
  • It fits VIPER components together like lock and key, without needing to force-cast between types.
  • It automates concepts like assembly and weak relationships so you don't have to.
  • It extracts assembly responsibility from the Router and grants it to the Module.
  • It uses Entities to define the dependencies of an Interactor, and Builder to provide dependency injection to the Router.
  • It designates the Interactor as the holder of state, and uses a PresenterModel to communicate between the business logic and presentation logic layers. Presenters use the PresenterModel to consult their state and update their ViewModel without exposing the Entity layer. This setup embraces modern practices like Reactive programming using Combine.

All this results in a VIPER architecture implementation that's simple and sophisticated. For that reason, it's simply called "VIPER".

What is an Ouroboros?

It's a uni-directional viper, as emblazoned on the logo.

Acknowledgements

  • Yan Heere for swiftly designing the delightful Ouroboros logo in Swift fashion.

  • OneSadCookie for the many lunchtime discussions about VIPER and the Ouroboros moniker.

Description

  • Swift Tools 5.1.0
View More Packages from this Author

Dependencies

Last updated: Sun Mar 17 2024 16:18:35 GMT-0900 (Hawaii-Aleutian Daylight Time)