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.
In true Swift fashion, VIPER is only available as a Swift Package.
Add the following to your Package.swift
's dependencies:
array:
.package(url: "[email protected]:thomverbeek/VIPER.git", from: "0.5.1"),
In your project or workspace, choose File ▸ Swift Packages ▸ Add Package Dependency…
to add https://github.com/thomverbeek/VIPER
as a package dependency.
This package comes bundled with viper-tools
, a command line utility.
cd
into this Swift package, and run the following command:$ swift run viper-tools
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
--exclude-directory
flag to generate files without a directory. This is very useful when grouping your VIPER modules into Swift Packages in your project.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:
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.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.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.
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
.
Router
and grants it to the Module
.Entities
to define the dependencies of an Interactor
, and Builder
to provide dependency injection to the Router
.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".
It's a uni-directional viper, as emblazoned on the logo.
Yan Heere for swiftly designing the delightful Ouroboros logo in Swift fashion.
OneSadCookie for the many lunchtime discussions about VIPER and the Ouroboros moniker.
link |
Stars: 13 |
Last commit: 2 years ago |
This release drastically alters VIPER from a unidirectional to bidirectional implementation:
AnyObject
requirement forces all VIPER objects to be implemented as classes. This allows for some Objective-C runtime magic to abstract message passing mechanics.Combine
is removed as a dependency, which lowers the minimum OS requirements. Message passing is handled under the hood via simple closures. The need for subscriptions is removed in favour of associated objects and function variable scopes.Combine
publishers for message passing.Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics