Home   →   Blog   →   The use and adaptation of the VIPER architecture on our own example of development, part 2

    The use and adaptation of the VIPER architecture on our own example of development, part 2

    This is the second part of the article about our experience of using and adapting the Viper architecture. In the first part, we discussed the basics, now it's time to talk about structuring and creating project skeleton for VIPER.

    General Info

    To simplify the development process and make it more clear, we usually separate classes into different folders and groups such as Constants, Extensions, Services, Models, Presentation. In addition, Extensions could be divided into smaller groups like UIExtensions, Animations, etc., and Models - into DTO, Business, etc. As for Services, we will discuss them below.

    As you can see, there is nothing particularly difficult about these groups, but let's take a closer look at the project structure in terms of VIPER architecture.

    1. Services

    For the code clarity, services are better to be divided into several groups like Infrastructure and Business. The first group should contain essential services (API service, Settings service, etc), and the second one consists of all services working with data and business logic.

    And there is also the main class called Service Assembly that is a shared instance comprising all services in the app e.g. API service, Settings service, business services, etc.

    protocol ServicesAssemblyProtocol {
       var application: UIApplication { get }
       var apiService: APIService { get }
       var weatherService: WeatherService { get }

    All services are initialized with the required instances of other ones. For example, UserService might need access to API or Database services.

    class ServicesAssemblyImpl: ServicesAssemblyProtocol {
       let application: UIApplication
       let apiService: APIService
       let weatherService: WeatherService
       init(application: UIApplication) {
           self.application = application
           apiService = APIServiceImpl()
           weatherService = WeatherServiceImpl(apiService: apiService)

    All setup logic for services is generally implemented in AppDelegate since we might need it at the very start.

       func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
           // Setup services
           ServicesAssembly.setup(application: application)
           // Setup everything else if needed
           return true

    2. Presentation

    Presentation is a group with all layout logic inside. Let’s examine its structure in depth.

    PresentationAssembly is a shared class containing references to the presentation layer.

    protocol PresentationAssemblyProtocol {
       var router: AppRouterProtocol! { get }
       var whisper: InAppNotificationsProvider! { get }
    class PresentationAssembly: PresentationAssemblyProtocol {
       static let shared = PresentationAssembly()
       var router: AppRouterProtocol!
       var whisper: InAppNotificationsProvider!
       func setup(withNavigation navigation: UINavigationController, modules: Array<ModuleFactoryProtocol>, urlScheme: String, services: ServicesAssemblyProtocol) {
           router = AppRouterImpl(withNavigation: navigation, modules: modules, urlScheme: urlScheme)
           whisper = InAppNotificationsProviderImpl(withNavigation: navigation)


    Let’s consider Application root more carefully.

    Application root

    Application root is an entry point to the app. It is called by AppDelegate and responsible for launching an application and all configuration needed before that moment. 

      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
           // Setup here       
           // Start application
           appRoot = ApplicationRoot(withNavigation: navigationController)
           return true

    It sets up every necessary service, router, presentation stuff and chooses a module to show first (log in or main screen, for instance). Application root also contains all existing modules inside in order to know about each of them to be able to create it.

    class ApplicationRoot: NSObject {
       let modules : Array<ModuleFactoryProtocol> = [
       // MARK:
       fileprivate let router: AppRouterProtocol
       fileprivate let whisper: InAppNotificationsProvider
       init(withNavigation navigation: UINavigationController) {
           let services =  ServicesAssembly.shared
           let presentation = PresentationAssembly.shared
           presentation.setup(withNavigation: navigation, modules: modules, urlScheme: "viper", services: services)
           router = presentation.router
           whisper = presentation.whisper   
       func start() {
           let urn = StartScreenFactory.shared.moduleURN
           router.pushModule(byUrn: urn, animated: true, completion: { (_) in

    Application router

    AppRouter contains all routing logic. It is created by Application root and responsible for navigation between modules e.g. push/pop or present/dismiss etc.

    protocol AppRouterProtocol {
       var navigationController: UINavigationController { get }
       func pushModule(byUrn urn: String, animated: Bool, completion: ModuleCompletionHandler?)    
       func presentModule(byUrn urn: String, animated: Bool, completion: ModuleCompletionHandler?)    
       func popToViewController(_ controller: UIViewController, animated: Bool)
       func dismissCurrentController(animated: Bool)

    It’s global in general but can be moved to each module or added to some of them depending on the complexity if we want so.

    As you can see Routing group also contains ModuleFactoryProtocol

    protocol ModuleFactoryProtocol {
        * Module URN ( ex. profile:{userID} )
       var moduleURN: String { get }    
        * Create module with arguments
        * Returns module root UIViewController, must implement ModuleInputProtocol.
       func createModule(arguments: NamedValuesType, completion: ModuleCompletionHandler?) -> UIViewController

    which defines the way how new module can be created and ModuleInputProtocol

    protocol ModuleInputProtocol {
        * Configure module with arguments.
        * Calls form Module factory
       func setupInitialState(withArguments args: NamedValuesType, completion: ModuleCompletionHandler?)

    defines the way module could be configured with additional parameters.

    Presentation group also contains any additional groups required by the app. For example, InAppNotifications (alerts, toast etc), StyleKit (responsible for styling application if necessary), SocialServices (Facebook, Twitter, etc), and lots of other logic your app may need.


    Views is created for keeping all the custom layouts which are placed just here. Besides, it can include the following groups inside such as Layouts, Cells, Collections, etc.

    User stories

    This part consists of all your screens and UI logic and contains every VIPER module that is located here and should include: Factory, Configurator, View, Presenter, and Interactor.

    We will describe the module structure in the next article and right now just say that it may have storyboard or xib file inside if needed.

    So, to conclude, to simplify finding the specific class or service let’s mention that the structure of the project is extremely clear. In addition, it’s good practice to keep the group and folder structure the same way.

    In the next - third - part of our article we'll tell you about creating a module in terms of VIPER architecture: units, their responsibilities, etc.

    Read our blog to know more!

    Уникальность ADVEGO - 100%
    Next Posts
    The use and adaptation of the VIPER architecture on...
    This is the third part of the article series about our...
    The use and adaptation of the VIPER architecture on...
    General info about software architectures
    The Use and Adaptation of the VIPER Architecture on...
    The article about using and adapting the Viper architecture