{"id":4402,"date":"2017-04-10T16:51:19","date_gmt":"2017-04-10T16:51:19","guid":{"rendered":"https:\/\/www.ckl.io\/?p=4402"},"modified":"2022-07-01T17:42:11","modified_gmt":"2022-07-01T17:42:11","slug":"best-practices-viper-architecture","status":"publish","type":"post","link":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/","title":{"rendered":"VIPER architecture: Our best practices to build an app like a boss"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">The dev team at Cheesecake Labs has been using VIPER for iOS and Android mobile app development for over one year and we just love this clean architecture! <\/span><\/p>\n<p><span style=\"font-weight: 400;\">This article summarizes our best practices on the VIPER architecture, using code examples from our VIPER&nbsp;<\/span><a href=\"https:\/\/github.com\/CheesecakeLabs\/Boilerplate_iOS_VIPER\"><span style=\"font-weight: 400;\">boilerplate<\/span><\/a><span style=\"font-weight: 400;\">. The code samples used here are in Swift, but all concepts mentioned may be applied to an Android project developed with VIPER, either using Java or Kotlin.<\/span><\/p>\n<p><!--more--><span style=\"font-weight: 400;\">If you still need to get familiar with basic VIPER concepts and how this architecture can make your code more organized and scalable, I suggest you read the following articles:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\"><a href=\"https:\/\/medium.com\/ios-os-x-development\/ios-architecture-patterns-ecba4c38de52\">Comparison between most common mobile apps design patterns<\/a><\/span><\/li>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/www.ckl.io\/blog\/ios-project-architecture-using-viper\"><span style=\"font-weight: 400;\">Great guide for your first steps on VIPER for iOS<\/span><\/a><\/li>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\"><a href=\"https:\/\/www.ckl.io\/blog\/using-viper-architecture-android\/\">Unique content on VIPER architecture for Android<\/a><\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">So, are you ready to learn our best practices on how to build an app with VIPER?<\/span><br \/>\n<img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/media.giphy.com\/media\/xUPGcKhqQRoOgfMeIM\/giphy.gif\" alt=\"Funny image of a man vibrating and saying let us go for VIPER\" width=\"401\" height=\"225\"><\/p>\n<h2><span style=\"font-weight: 400;\">Project structure for real VIPER modules decoupling<\/span><\/h2>\n<figure style=\"width: 357px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" src=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/04\/2aalvv8.png\" alt=\"Image of Xcode Navigation Bar with the folder structure for iOS VIPER project\" width=\"357\" height=\"551\"><figcaption class=\"wp-caption-text\"><\/p>\n<p><center>VIPER&#8217;s folder structure for iOS<\/center><\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">You can see that we keep all VIPER\u2019s modules <\/span><b>as decoupled as possible<\/b><span style=\"font-weight: 400;\">, saving us from future headaches when the project grows or specification changes. If you delete any of them, you should get several errors just on the Routers that reference that model &#8211; but not on Views, Presenters, Interactors, Data Managers or Entity classes.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">One key point to help you truly decouple your modules is to <\/span><b>keep all entities on a separate folder<\/b><span style=\"font-weight: 400;\">, linking them to the project itself and not to any specific module.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Also, using <\/span><b>Data Managers<\/b><span style=\"font-weight: 400;\"> to perform API requests and manipulate local database is an excellent way of increasing the project organization, but requires some attention:<\/span><\/p>\n<ol>\n<li>Keep Data Managers inside the Utils folder, separating&nbsp;them from the modules;<\/li>\n<li>Group methods for similar entities &#8211; such as <i><span style=\"font-weight: 400;\">User<\/span><\/i><span style=\"font-weight: 400;\">, <\/span><i><span style=\"font-weight: 400;\">Profile<\/span><\/i><span style=\"font-weight: 400;\"> and <\/span><i><span style=\"font-weight: 400;\">CompanyProfile &#8211;<\/span><\/i><span style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">&nbsp;into one Data Manager to avoid the overhead of creating one Data Manager for each entity;<\/span><\/span><\/li>\n<li>Split each Data Manager into Local and API classes to make the code on the Interactors much more readable:<\/li>\n<\/ol>\n<pre><code class=\"language-swift\">\nclass MainSearchInteractor {\n    \n    \/\/ Properties\n    weak var output: MainSearchInteractorOutput?\n    var apiDataManager = ProfileApiDataManager()\n    var localDataManager = ProfileLocalDataManager()\n}\n\nextension MainSearchInteractor: MainSearchUseCase {\n  \n    \/\/ Code below show how interactor get data from API and then saves it on local DB with separate data managers\n    func searchProducts(with searchTerm: String, onPage page: Int) {\n        \n        self.apiDataManager.searchProducts(with: searchTerm, forPage: page) { (products) in\n            if let products = products {\n                self.localDataManager.updateSearchResultFavorites(products) { (products) in\n                    self.output?.onFetchProductsSuccess(Array(products), shouldAppend: page != 1)\n                }\n            } else {\n                self.output?.onFetchProductsSuccess(nil, shouldAppend: page != 1)\n            }\n        }\n    }\n}\n<\/code><\/pre>\n<h2><span style=\"font-weight: 400;\">Generating VIPER&#8217;s files faster and furiously<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">If you&#8217;ve already developed using VIPER, you&#8217;ve had the bad experience of creating all the 20+ Swift files needed for a simple screen with three tabs on the navigation bar. But there&#8217;s a light at the end of the tunnel: <\/span><span style=\"font-weight: 400;\">this amazing&nbsp;<\/span><span style=\"font-weight: 400;\"><a href=\"https:\/\/github.com\/natangr\/ViperTemplate\"><strong>Xcode plugin<\/strong><\/a> which automates the generation of all VIPER files for one module with three clicks.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If you think that that\u2019s too much, meet <\/span><a href=\"https:\/\/github.com\/rambler-digital-solutions\/Generamba\"><b>Generamba<\/b><\/a><b>:<\/b><span style=\"font-weight: 400;\"> a code generator designed to create VIPER modules from the terminal, quite easy to customize for any other classes.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Define contracts with protocols<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Just like for us humans, contracts on VIPER are a voluntary agreement between two parties (module components) concerning the rights (methods) and duties (arguments) that arise from agreements.<\/span><span style=\"font-weight: 400;\"> At Cheesecake Labs, <strong>we use protocols<\/strong> to define the methods that a module component can call from other components on the same module. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">However, before start writing the code for a new View or Presenter, for example, think about the information flow between both components and declare their methods on the Contract first.<\/span><\/p>\n<pre><code class=\"language-swift\">\n\/\/ MainSearchContract.swift\nimport Foundation\n\nprotocol MainSearchView: BaseView {\n    func showCustomError(_ message: String?)\n    func updateVisibility(onSearchController willBeActive: Bool)\n    func showSearchResult(_ products: [Product]?, shouldAppend: Bool)\n}\n\nprotocol MainSearchPresentation: class {\n    func onViewDidLoad()\n    func onWillPresentSearchController()\n    func onSearchTermChanged(to searchTerm: String)\n    func onProductFavoriteChanged(_ product: Product, to isFavorite: Bool)\n    func onProductSelected(_ product: Product)\n    func onInfiniteScrollTriggered()\n}\n\nprotocol MainSearchUseCase: class {\n    func searchProducts(with searchTerm: String, onPage page: Int)\n    func updateProductFavorited(_ product: Product, to newValue: Bool)\n}\n\nprotocol MainSearchInteractorOutput: class {\n    func onFetchProductsSuccess(_ products: [Product]?, shouldAppend: Bool)\n    func onFetchProductsFailure(message: String)\n}\n\nprotocol MainSearchWireframe: class {\n    func showProductScreen(delegate: ProductScreenDelegate, product: Product?)\n}\n<\/code><\/pre>\n<p><span style=\"font-weight: 400;\">The Xcode plugin mentioned before will also create a <\/span><i><span style=\"font-weight: 400;\">ModuleNameContract.swift<\/span><\/i><span style=\"font-weight: 400;\"> file with all protocols, waiting for your declaration of the necessary methods. Once those protocols are defined, you have complete control of the information flow between the components of a VIPER module.<\/span><\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/media.giphy.com\/media\/l0HlAjLE2947PJlGo\/giphy.gif\" alt=\"Image of a judge showing a contract paper and asking whose signature is that, making reference to a VIPER contract\" width=\"480\" height=\"270\"><\/p>\n<h2><span style=\"font-weight: 400;\">Automate modules initialization on the Router<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Before presenting the View of a VIPER module, you need to make sure all components have been properly initialized. I can think of at least 3 very different ways of doing it, but the flow below is the best option we&#8217;ve came up with. The ace in the hole here is to have a <\/span><b>static function<\/b><span style=\"font-weight: 400;\"> on each Router to initialize its own module together with some <code>UIViewController<\/code> and <code>UIStoryboard<\/code> extensions. Then, if module A wants to present Module B:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">Module A\u2019s Router will call Module B\u2019s static function to initialize all of its components, returning a View.<\/span><\/li>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">Module A\u2019s Router presents Module B\u2019s View.<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">As simple as that. Having the module initialization code on its own Router will eliminate a bunch of code repetition, specially for huge projects. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">You need to create&nbsp;these extensions once<\/span><span style=\"font-weight: 400;\">:<\/span><\/p>\n<pre><code class=\"language-swift\">\n\/\/ ReusableView.swift\nprotocol ReusableView: class {}\n\nextension ReusableView {\n    static var reuseIdentifier: String {\n        return String(describing: self)\n    }\n}\n<\/code><\/pre>\n<pre><code class=\"language-swift\">\n\/\/ UIViewController.swift\nextension UIViewController: ReusableView { }\n<\/code><\/pre>\n<pre><code class=\"language-swift\">\n\/\/ UIStoryboard.swift\nextension UIStoryboard {\n    func instantiateViewController() -&gt; T where T: ReusableView {\n        return instantiateViewController(withIdentifier: T.reuseIdentifier) as! T\n    }\n}\n<\/code><\/pre>\n<p><span style=\"font-weight: 400;\">And then, leave initialization code on the router of each VIPER module:<\/span><\/p>\n<pre><code class=\"language-swift\">\n\/\/ MainSearchRouter.swift\nclass MainSearchRouter {\n\n    \/\/ MARK: Properties\n    weak var view: UIViewController?\n\n    \/\/ MARK: Static methods\n    static func setupModule() -&gt; MainSearchViewController {\n        let viewController = UIStoryboard(name: MainSearchViewController.storyboardName, bundle: nil).instantiateViewController() as MainSearchViewController\n        let presenter = MainSearchPresenter()\n        let router = MainSearchRouter()\n        let interactor = MainSearchInteractor()\n\n        viewController.presenter =  presenter\n\n        presenter.view = viewController\n        presenter.router = router\n        presenter.interactor = interactor\n\n        router.view = viewController\n\n        interactor.output = presenter\n\n        return viewController\n    }\n}\n<\/code><\/pre>\n<p><span style=\"font-weight: 400;\">It might seem like a lot of steps, but good news: the aforementioned plugin <strong>automates that<\/strong> for us as well! \ud83d\ude42<\/span><\/p>\n<p><span style=\"font-weight: 400;\">However, you&#8217;ll need to take some additional steps if you want to fit a <code>UITabBarController<\/code> or a <code>UIPageViewController<\/code> into the VIPER architecture. If you need any help, just drop a comment on this post and I&#8217;ll prepare a specific Gist for you.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If you&#8217;ve came up this far, dear reader, you&#8217;re really avid for knowledge. So I&#8217;ll give you 3 advices to make sure you&#8217;ve fully understood the responsibilities of the Router:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">If you need to <\/span><b>open a URL<\/b><span style=\"font-weight: 400;\"> when the user clicks a button, call <code>UIApplication<\/code>.<code>shared<\/code>.<code>openURL(url)<\/code> on the Router because you&#8217;re navigating (i.e. routing) out of your current module;<br \/>\n<\/span><\/li>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">The same concept applies for <\/span><b>social media sharing<\/b><span style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">: call <code>UIActivityViewController<\/code> from the Router because iOS will send the user to a View or app out of your current module;<\/span><\/span><\/li>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">If you&#8217;re only <\/span><b>calling an Action Sheet<\/b><span style=\"font-weight: 400;\"> to get some user input, that\u2019s an UI component added to your current module. So you can call it from your View, and enjoy straightforward callbacks from the <code>UIAlertController<\/code>.<\/span><\/li>\n<\/ol>\n<h2><span style=\"font-weight: 400;\">Use delegates to send data between VIPER modules<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">You&#8217;ve probably faced a situation where a field on Module A is filled with the selected item of the Module B. So Module A calls Module B when the user clicks the field, and Module B returns the selected item to the existing Module A through the delegate.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Delegates are an awesome approach to send information back and forth between VIPER modules:<\/span><\/p>\n<pre><code class=\"language-swift\">\n\/\/ 1. Declare which messages can be sent to the delegate\n\n\/\/ ProductScreenDelegate.swift\nprotocol ProductScreenDelegate {<\/code><\/pre>\n<pre><code class=\"language-swift\">    \/\/Add arguments if you need to send some information\n    func onProductScreenDismissed()\n    func onProductSelected(_ product: Product?)\n}\n\n\/\/ 2. Call the delegate when you need to send him a message\n\n\/\/ ProductPresenter.swift\nclass ProductPresenter {\n\n    \/\/ MARK: Properties\n    weak var view: ProductView?\n    var router: ProductWireframe?\n    var interactor: ProductUseCase?\n    var delegate: ProductScreenDelegate?\n}\n\nextension ProductPresenter: ProductPresentation {\n\n    \/\/View tells Presenter that view disappeared\n    func onViewDidDisappear() {\n\n        \/\/Presenter tells its delegate that the screen was dismissed\n        delegate?.onProductScreenDismissed()\n    }\n}\n\n\/\/ 3. Implement the delegate protocol to do something when you receive the message\n\n\/\/ ScannerPresenter.swift\nclass ScannerPresenter: ProductScreenDelegate {\n\n    \/\/Presenter receives the message from the sender\n    func onProductScreenDismissed() {\n\n        \/\/Presenter tells view what to do once product screen was dismissed\n        view?.startScanning()\n    }\n    ...\n}\n\n\/\/ 4. Link the delegate from the Product presenter in order to proper initialize it\n\n\/\/ File ScannerRouter.swift\nclass ProductRouter {\n\n    static func setupModule(delegate: ProductScreenDelegate?) -&gt; ProductViewController {\n        ...\n        let presenter = ScannerPresenter()\n\n        presenter.view = view\n        presenter.interactor = interactor\n        presenter.router = router\n        presenter.delegate = delegate \/\/ Add this line to link the delegate\n        ...\n        }\n}\n<\/code><\/pre>\n<p>&nbsp;<\/p>\n<h2><span style=\"font-weight: 400;\">And avoid dictionaries to send information between VIPER components<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Using a POSO (Plain Old Swift Object) to send information between VIPER\u2019s components is the best approach if you want to be 100% compliant with the VIPER architecture. But <strong>sending the Entity <\/strong>itself between VIPER components works fine and removes the overhead of creating POSOs.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Anyway, avoid sending this data using dictionaries if you don&#8217;t want to get lost with key names when your project starts growing and changing.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Focus on the VIPER mindset<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">If you want to take the most of this architecture, it&#8217;s important to keep your team completely in sync with the responsibilities of each component of a VIPER module. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">Even after understanding the role of each specific component, our team still faced some doubts, mostly influenced by previous experience with MVC.<\/span><\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/media.giphy.com\/media\/3orif4Q0OuQcu5Gges\/giphy.gif\" alt=\"Homer Simpson is telling other people that he wants to make a few things clear, making reference to VIPER responsabilities.\" width=\"397\" height=\"304\"><\/p>\n<ol>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">The View is the one that <strong>handles UI elements<\/strong>: it imports <\/span><i><code>UIKit<\/code><\/i><span style=\"font-weight: 400;\"> and implements all logic regarding UI elements from its module. <\/span><i><span style=\"font-weight: 400;\">TableView<\/span><\/i><span style=\"font-weight: 400;\"> logics, for example, are implemented on the View. If you want to make your code more readable, split <\/span><i><span style=\"font-weight: 400;\">TableView<\/span><\/i><span style=\"font-weight: 400;\"> logics on extensions. If you want to make your project even more concise, use a <\/span><i><span style=\"font-weight: 400;\">TableViewDataManager;<\/span><\/i><\/li>\n<\/ol>\n<ol start=\"2\">\n<li><span style=\"font-weight: 400;\"> The presenter does not import <\/span><i><code>UIKit<\/code><\/i><span style=\"font-weight: 400;\"> and <strong>does not handle UI elements<\/strong>, but it does prepare the data in the format required by the view and take decisions based on UI events from the view. Do <\/span>not<span style=\"font-weight: 400;\"> manipulate any UI element on the presenter, it shouldn&#8217;t handle them;<\/span><\/li>\n<\/ol>\n<ol start=\"3\">\n<li><span style=\"font-weight: 400;\"> The Interactor can also prepare the data, but for the database. Once the <\/span><i><span style=\"font-weight: 400;\">ApiDataManager<\/span><\/i><span style=\"font-weight: 400;\"> fetches some data, for example, the Interactor can do some sorting or filtering before asking the <\/span><i><span style=\"font-weight: 400;\">LocalDataManager<\/span><\/i><span style=\"font-weight: 400;\"> to save the data. But note that the Interactor <strong>doesn&#8217;t know the view<\/strong>, so it has no idea how the data should be prepared for the view.<\/span><\/li>\n<\/ol>\n<h2><span style=\"font-weight: 400;\">Wrapping up<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">The product team decided to drop out a feature from your project? Or your small project started growing huge? Use proper VIPER architecture and avoid future headaches! <\/span><\/p>\n<p><span style=\"font-weight: 400;\">Automating VIPER <\/span><a href=\"https:\/\/github.com\/natangr\/ViperTemplate\"><span style=\"font-weight: 400;\">files creation and modules initialization <\/span><\/a><span style=\"font-weight: 400;\">will eliminate the overhead of working with this &#8211; complex at first sight &#8211; but clear and awesome architecture. Android developers <\/span><a href=\"https:\/\/www.ckl.io\/blog\/using-viper-architecture-android\"><span style=\"font-weight: 400;\">can also use it<\/span><\/a><span style=\"font-weight: 400;\"> as well.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We&#8217;ve seen that our approach to VIPER architecture is actually composed of VIPRC modules (View-Interactor-Presenter-Router-Contract), while Entities are decoupled from the modules, along with Data Managers. I know the name VIPRC is not sexy at all, but it allows you to build an app like a boss. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">Do you have any other tips for using VIPER architecture on iOS and Android app development? Feel free to share your experience!<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The dev team at Cheesecake Labs has been using VIPER for iOS and Android mobile app development for over one year and we just love this clean architecture! This article summarizes our best practices on the VIPER architecture, using code examples from our VIPER&nbsp;boilerplate. The code samples used here are in Swift, but all concepts [&hellip;]<\/p>\n","protected":false},"author":65,"featured_media":4432,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[432,7],"tags":[305,21,15],"class_list":["post-4402","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-engineering","category-opinion","tag-tag-development","tag-tag-ios","tag-tag-mobile"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>VIPER architecture: Our best practices to build an app like a boss<\/title>\n<meta name=\"description\" content=\"Cheesecake Labs has been using VIPER for iOS and Android mobile app development for over one year. We just love this architecture! Read our best practices.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"VIPER architecture: Our best practices to build an app like a boss\" \/>\n<meta property=\"og:description\" content=\"Cheesecake Labs has been using VIPER for iOS and Android mobile app development for over one year. We just love this architecture! Read our best practices.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/\" \/>\n<meta property=\"og:site_name\" content=\"Cheesecake Labs\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/cheesecakelabs\" \/>\n<meta property=\"article:published_time\" content=\"2017-04-10T16:51:19+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-07-01T17:42:11+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/04\/Banner_viper3.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"720\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Cheesecake Labs\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@cheesecakelabs\" \/>\n<meta name=\"twitter:site\" content=\"@cheesecakelabs\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/\"},\"author\":{\"name\":\"Marcelo Gracietti\"},\"headline\":\"VIPER architecture: Our best practices to build an app like a boss\",\"datePublished\":\"2017-04-10T16:51:19+00:00\",\"dateModified\":\"2022-07-01T17:42:11+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/\"},\"wordCount\":1427,\"commentCount\":0,\"image\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/04\/Banner_viper3.png\",\"keywords\":[\"development\",\"iOS\",\"Mobile\"],\"articleSection\":[\"Engineering\",\"Opinion\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/\",\"url\":\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/\",\"name\":\"VIPER architecture: Our best practices to build an app like a boss\",\"isPartOf\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/04\/Banner_viper3.png\",\"datePublished\":\"2017-04-10T16:51:19+00:00\",\"dateModified\":\"2022-07-01T17:42:11+00:00\",\"author\":{\"@type\":\"person\",\"name\":\"Marcelo Gracietti\"},\"description\":\"Cheesecake Labs has been using VIPER for iOS and Android mobile app development for over one year. We just love this architecture! Read our best practices.\",\"breadcrumb\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#primaryimage\",\"url\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/04\/Banner_viper3.png\",\"contentUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/04\/Banner_viper3.png\",\"width\":2000,\"height\":720},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cheesecakelabs.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"VIPER architecture: Our best practices to build an app like a boss\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#website\",\"url\":\"https:\/\/cheesecakelabs.com\/blog\/\",\"name\":\"Cheesecake Labs\",\"description\":\"Nearshore outsourcing company for Web and Mobile design and engineering services, and staff augmentation for startups and enterprises..\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cheesecakelabs.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"name\":\"Marcelo Gracietti\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2016\/11\/Marcelo-Gracietti-1.png\",\"contentUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2016\/11\/Marcelo-Gracietti-1.png\",\"caption\":\"Marcelo Gracietti\"},\"description\":\"10 years of experience in Marketing and Sales in the Technology sector. My main purpose is help, support and structure efficient operations and also develop independent and multidisciplinary teams.\",\"url\":\"https:\/\/cheesecakelabs.com\/blog\/autor\/tchello\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"VIPER architecture: Our best practices to build an app like a boss","description":"Cheesecake Labs has been using VIPER for iOS and Android mobile app development for over one year. We just love this architecture! Read our best practices.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/","og_locale":"en_US","og_type":"article","og_title":"VIPER architecture: Our best practices to build an app like a boss","og_description":"Cheesecake Labs has been using VIPER for iOS and Android mobile app development for over one year. We just love this architecture! Read our best practices.","og_url":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/","og_site_name":"Cheesecake Labs","article_publisher":"https:\/\/www.facebook.com\/cheesecakelabs","article_published_time":"2017-04-10T16:51:19+00:00","article_modified_time":"2022-07-01T17:42:11+00:00","og_image":[{"width":2000,"height":720,"url":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/04\/Banner_viper3.png","type":"image\/png"}],"author":"Cheesecake Labs","twitter_card":"summary_large_image","twitter_creator":"@cheesecakelabs","twitter_site":"@cheesecakelabs","twitter_misc":{"Written by":null,"Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#article","isPartOf":{"@id":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/"},"author":{"name":"Marcelo Gracietti"},"headline":"VIPER architecture: Our best practices to build an app like a boss","datePublished":"2017-04-10T16:51:19+00:00","dateModified":"2022-07-01T17:42:11+00:00","mainEntityOfPage":{"@id":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/"},"wordCount":1427,"commentCount":0,"image":{"@id":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#primaryimage"},"thumbnailUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/04\/Banner_viper3.png","keywords":["development","iOS","Mobile"],"articleSection":["Engineering","Opinion"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/","url":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/","name":"VIPER architecture: Our best practices to build an app like a boss","isPartOf":{"@id":"https:\/\/cheesecakelabs.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#primaryimage"},"image":{"@id":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#primaryimage"},"thumbnailUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/04\/Banner_viper3.png","datePublished":"2017-04-10T16:51:19+00:00","dateModified":"2022-07-01T17:42:11+00:00","author":{"@type":"person","name":"Marcelo Gracietti"},"description":"Cheesecake Labs has been using VIPER for iOS and Android mobile app development for over one year. We just love this architecture! Read our best practices.","breadcrumb":{"@id":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#primaryimage","url":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/04\/Banner_viper3.png","contentUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/04\/Banner_viper3.png","width":2000,"height":720},{"@type":"BreadcrumbList","@id":"https:\/\/cheesecakelabs.com\/blog\/best-practices-viper-architecture\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cheesecakelabs.com\/blog\/"},{"@type":"ListItem","position":2,"name":"VIPER architecture: Our best practices to build an app like a boss"}]},{"@type":"WebSite","@id":"https:\/\/cheesecakelabs.com\/blog\/#website","url":"https:\/\/cheesecakelabs.com\/blog\/","name":"Cheesecake Labs","description":"Nearshore outsourcing company for Web and Mobile design and engineering services, and staff augmentation for startups and enterprises..","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cheesecakelabs.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","name":"Marcelo Gracietti","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cheesecakelabs.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2016\/11\/Marcelo-Gracietti-1.png","contentUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2016\/11\/Marcelo-Gracietti-1.png","caption":"Marcelo Gracietti"},"description":"10 years of experience in Marketing and Sales in the Technology sector. My main purpose is help, support and structure efficient operations and also develop independent and multidisciplinary teams.","url":"https:\/\/cheesecakelabs.com\/blog\/autor\/tchello\/"}]}},"_links":{"self":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts\/4402","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/users\/65"}],"replies":[{"embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/comments?post=4402"}],"version-history":[{"count":1,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts\/4402\/revisions"}],"predecessor-version":[{"id":10299,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts\/4402\/revisions\/10299"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/media\/4432"}],"wp:attachment":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/media?parent=4402"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/categories?post=4402"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/tags?post=4402"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}