Facing the VIPER

F2f5f7bc8bd3bd71e51d303e9881fe78?s=47 Esteban Torres
September 15, 2017

Facing the VIPER

What I learned while implementing VIPER on a side project; all the pitfalls you might want to avoid and why you might to also give it a try.

F2f5f7bc8bd3bd71e51d303e9881fe78?s=128

Esteban Torres

September 15, 2017
Tweet

Transcript

  1. POP MARIN TODOROV 1 — Esteban Torres • NSSpain 2017

    • @esttorhe
  2. POP FACING THE VIPER MARIN TODOROV 2 — Esteban Torres

    • NSSpain 2017 • @esttorhe
  3. POP FACING THE VIPER MARIN TODOROV ESTEBAN TORRES • IOS

    @ SOUNDCLOUD 3 — Esteban Torres • NSSpain 2017 • @esttorhe
  4. MARIN IS ! HE TRIED UNTIL THE VERY LAST MINUTE

    BUT GOT WORST AND COULDN'T MAKE IT IN THE END 4 — Esteban Torres • NSSpain 2017 • @esttorhe
  5. FACING THE VIPER ESTEBAN TORRES • IOS @ SOUNDCLOUD 5

    — Esteban Torres • NSSpain 2017 • @esttorhe
  6. So we have around 30 minutes where I'll bore you

    with how I tried and failed and tried again and midly succeeded in learning VIPER AGENDA 6 — Esteban Torres • NSSpain 2017 • @esttorhe
  7. I'm going to quickly sumarise what my talk will consist

    of. I know that every other week there seems to be yet another talk or article about which architecture you should or shouldn't use; so bear with me while I try to explain if this is yet another talk just like those or how it differs Big disclaimer here; I'm not an expert in VIPER and this is more a talk regarding what I LEARNED while implementing VIPER in a small project Even though this is not entirely a 100% VIPER talk per se; we can't go through it without actually getting our hands a little bit dirty with at least the basic concepts of VIPER This talk and its accompanying blog post started because I wanted to learn VIPERin more detail; understand is concept and incorporate Clean Code into my day to day life. In order to do so I decided to create a small simple project/app and build it using VIPER to try and teach me its principles. I'll try to carefully explain my ! of thought when first encountering this challenge and how I approach each step of it. Once we have gone through the «how?» it might do us some good to understand the «why?». And there's no better way to understadn the «why» of something than by enumerating all its benefits (or the benefits that you perceived) while implementing or using it. As with antyhing this is not a silver bullet; so I'll try to summarize what issues I found while implemementing my app following VIPER's principles. And finally I'll give you a wrap up of how I felt about this architecture; how it 1. Not another VIPER talk… 2. What is VIPER!? 3. Learning through experience 4. What benefits did I get? 5. What problems did I encounter? 6. So… VIPER then? 7 — Esteban Torres • NSSpain 2017 • @esttorhe
  8. So… let clear things up NOT ANOTHER VIPER TALK… 8

    — Esteban Torres • NSSpain 2017 • @esttorhe
  9. So yeah; I think it was pretty obvious if you

    think about the name of the talk, and it containing VIPER in it THIS IS ANOTHER VIPER TALK 9 — Esteban Torres • NSSpain 2017 • @esttorhe
  10. What… As I mentioned initially; I'm not an expert in

    VIPER; nor am I claiming to be one And this talk is more related to how ended up implementing VIPER, what I learned; what mistakes I made and perhaps prevent you from doing the same mistakes NO, IT IS NOT ANOTHER VIPER TALK 10 — Esteban Torres • NSSpain 2017 • @esttorhe
  11. OK, so we are slowly starting to ease into the

    main topic of the talk. Let's start slowly, and at the beginning because… where else would you begin WHAT IS VIPER? 11 — Esteban Torres • NSSpain 2017 • @esttorhe
  12. …our application of Clean Architecture to iOS apps. …is a

    backronym for View, Interactor, Presenter, Entity and Routing. — Jeff Gilbert & Conrad Stoll1 1 https://mutualmobile.com/posts/meet-viper-fast-agile-non-lethal-ios-architecture-framework 12 — Esteban Torres • NSSpain 2017 • @esttorhe
  13. The blog post that incited this talk started as a

    result of my past failed attempts at VIPER Some time ago I first started learning about VIPER; probably around the time when all the talks where related to this architecture And as many other developers my first couple of tries were fruitless… ! 13 — Esteban Torres • NSSpain 2017 • @esttorhe
  14. My first impression of VIPER some time ago when I

    first tried to implement it was that there were too many layers; where things should go, why so many layers… it was pretty discouraging and quite overwhelming… didn't have enough time back then to spend a decent amount of time to learn this concept and thus I let the topic go. Recently I decided that it was time to finally tame the VIPER LAYERS!? 14 — Esteban Torres • NSSpain 2017 • @esttorhe
  15. I decided it was enough and that I should charm

    the VIPER 15 — Esteban Torres • NSSpain 2017 • @esttorhe
  16. We already covered the pretty basic definition of what VIPER

    is; just Clean Code architecture applied to iOS and that its a backronym for VIEW, Interactor, Presenter, Entity & Routing Ok, time to get our hands dirty. VIPER and/or Clean Code Architecture takes a lot of inspiration from the «onion» architecture The basic principle of Onion Architecture is to follow the boundaries of these layers – the inner layer canʼt depend on its outer layer but can depend on layers beneath. For example, domain layer canʼt depend on Infrastructure layer, but Infrastructure layer can depend on Domain layer. 16 — Esteban Torres • NSSpain 2017 • @esttorhe
  17. OK, we now know where does the architecture takes inspiration

    from, what it stands for and a little bit of how the dependency flow goes. Now lets try to dig a little bit depper into each layer An Interactor, which contains the business logic as specified by a use case. A Presenter, which contains view logic for preparing content for display (as received from the Interactor) and reacting to user inputs (by requesting new data from the Interactor). A View, which displays what it is told to by the Presenter and relays user input back to the Presenter. VIPER ▸ View: displays what it is told to by the Presenter and relays user input back to the Presenter. ▸ Interactor: business logic. ▸ Presenter: contains view logic 17 — Esteban Torres • NSSpain 2017 • @esttorhe
  18. Entity Entities are the model objects manipulated by an Interactor.

    Entities are only manipulated by the Interactor. The Interactor never passes Entities to the presentation layer (i.e. Presenter). Wireframe Routing handles navigation from one screen to another as defined in the wireframes created by an interaction designer. A Wireframe object in responsible for routing. The Wireframe object owns the UIWindow, UINavigationController, etc. It is responsible for creating an Interactor, Presenter and View/ViewController and installing the ViewController in the window. Since the Presenter contains the logic to react to user inputs, it is the Presenter that knows when to navigate to another screen. The Wireframe knows how to navigate. So, the Presenter is a client of the Wireframe. VIPER ▸ Entity: model objects manipulated by an Interactor ▸ Wireframe/Routing: handles navigation from one screen to another 18 — Esteban Torres • NSSpain 2017 • @esttorhe
  19. Here we have a diagram of how all the components

    in VIPER interconnect with each other 19 — Esteban Torres • NSSpain 2017 • @esttorhe
  20. The basic principle of Onion Architecture is to follow the

    boundaries of these layers – the inner layer canʼt depend on its outer layer but can depend on layers beneath. For example, domain layer canʼt depend on Infrastructure layer, but Infrastructure layer can depend on Domain layer. So; how do we overcome this limitation when implementing VIPER. If you have your outer layers depending on the inner ones but not the oppsite is hard to send messages from the outmost part of the «onion» and then wait for the results to be notified back DEPENDENCY FLOW 20 — Esteban Torres • NSSpain 2017 • @esttorhe
  21. In order to circumvent this we need to use heavy

    usage of the concept of the «Dependency Inversion» principle. DEPENDENCY Inversion 21 — Esteban Torres • NSSpain 2017 • @esttorhe
  22. If you don't know or can't remember; fortunately for you

    I have copied the definition from Wikipedia …the conventional dependency relationships established from high-level… modules to low-level, dependency modules are reversed…. — Dependency Inversion - Wikipedia 2 2 https://en.wikipedia.org/wiki/Dependency_inversion_principle 22 — Esteban Torres • NSSpain 2017 • @esttorhe
  23. As you can see; by defining interfaces (or in our

    case protocols) we can then invert the dependencies and decouple the outer layers from the inner layers without losing the ability for the inner layers to communicate results outwards of the «onion». 23 — Esteban Torres • NSSpain 2017 • @esttorhe
  24. Let's start diving a little bit into my own experience

    with VIPER; as soon as you read the dependency flow present in VIPER you'll quickly realise that the only way then to achieve the direction flow of the dependencies is via defining «boundary» protocols in between the layers; that way you can actually reference outer layers from inner layers when notifying back the results from your computations This will give us a lot of benefits; not only for the Dependency Inversion Principle mentioned before; which is needed anyways in order to achieve this architecture; but it will allow us to better test our app. HERE BE protocols 24 — Esteban Torres • NSSpain 2017 • @esttorhe
  25. Yesterday Dave «rained on TDD's parade» by mentioning many good

    reasons why you should be pragmatic about what to test One of Clean Code Architecture «dogmas» is to TDD, or better put, the ability to easily TDD your code Also by defining your protocols first you start thinking about how the API for the components is going to look, then your unit tests will become the first consumers of your API and they can show you how good or bad an API is written even before there's an actual implementation. By doing this you avoid coding a component just to then reach the next component that's dependent on the previous to find out that you either need to pass some extra information, you need a different set of values returned or a combination of all the possibilities. This wasn't my first application developed relying on TDD but this was by far the easiest to set following this development process. Also super important to mention: By defining all your dependencies as protocols you set yourselve in the right path to mock said dependencies and then inject them when writing your tests to better control the setup of your unit tests TDD & protocols 25 — Esteban Torres • NSSpain 2017 • @esttorhe
  26. I'm not advocating that you should go back home and

    TDD the heck out of all your future apps. I'm just telling you what VIPER does to help you achieve certain things; like in this case, do TDD in a nice way TDD & protocols ⾠ DISCLAIMER⾠ 26 — Esteban Torres • NSSpain 2017 • @esttorhe
  27. Now that we have settle the foundations for what VIPER

    is; how it works, how everything interconnects with each other, how to overcome the dependencies problem is time to «Learn through experience» Learning through EXPERIENCE™ 27 — Esteban Torres • NSSpain 2017 • @esttorhe
  28. For the sakes of me learning VIPER I decided to

    do a fairly simple app Our APP 28 — Esteban Torres • NSSpain 2017 • @esttorhe
  29. Which is NOT a TODO app Decided against a TODO

    app because it seems like all the tutorials and articles on internet are based on building a TODO app; and while this would have seemed good because that means more documentation available, more examples, maybe same patterns that you could use to learn and improve it would somehow mean to me a «hindrance» in my learning experience by limiting the creative process Our APP NOT A TODO APP 29 — Esteban Torres • NSSpain 2017 • @esttorhe
  30. Instead I decided that I wanted to build a simple

    app that could connect to an endpoint A SINGLE endpoint, retrieves a JSON, parses said JSON to Entities, loads the images that are referenced in the Entities and let's the user tap and show the «detail» image User can enter a search criteria that will be used to as a seed to search for images in ther server and display results. IMAGEGRAM 30 — Esteban Torres • NSSpain 2017 • @esttorhe
  31. Let's enumerate then and identify each part First we'll need

    a networking component that will be able to connect to a server and consume an endpoint in said server. IMAGEGRAM 1. Connect to an endpoint (Network) 31 — Esteban Torres • NSSpain 2017 • @esttorhe
  32. Then we want to be able to convert the information

    retrieved from the endpoint to valid entities that we could manipulate in our application. We'll be getting JSON back from the server so we'll need a way to transform that into our objects IMAGEGRAM 1. Connect to an endpoint (Network) 2. Parse JSON to Entities 32 — Esteban Torres • NSSpain 2017 • @esttorhe
  33. This step is pretty trivial in terms of coding; we

    need to load some images that are referenced in the Entities as URLs Just need to remember Dave's comment about mega- corps interview questions; do not load on the main thread and don't force unwrap and we'll be good to go IMAGEGRAM 1. Connect to an endpoint (Network) 2. Parse JSON to Entities 3. Load the images in the Entities 33 — Esteban Torres • NSSpain 2017 • @esttorhe
  34. This goes hand in hand with the previous point IMAGEGRAM

    1. Connect to an endpoint (Network) 2. Parse JSON to Entities 3. Load the images in the Entities 4. Display the images in a grid 34 — Esteban Torres • NSSpain 2017 • @esttorhe
  35. And the last bit; we'll want to be able to

    identify when a user wants to see a detailed view of an image; so whenever a user taps on a cell/ image we'll load a new VC showing the image but bigger IMAGEGRAM 1. Connect to an endpoint (Network) 2. Parse JSON to Entities 3. Load the images in the Entities 4. Display the images in a grid 5. Show a detail view 35 — Esteban Torres • NSSpain 2017 • @esttorhe
  36. Now is time to identify what we need to achieve

    and see how that maps into VIPER First of all the Network here is a Data Store. The Data Store can be a web service, database, etc & is responsible for providing Entities to an Interactor. As anInteractor applies its business logic it will typically retrieve Entities from the Data Store, manipulate the Entities and then put the updated Entities back in the Data Store. The Data Store manages the persistence of the Entities. Entities do not know about the Data Store, so Entities do not know how to persist themselves. IMAGEGRAM 1. Connect to an endpoint (Network) ✅ 2. Parse JSON to Entities ✅ 3. Load the images in the Entities 4. Display the images in a grid 5. Show a detail view 36 — Esteban Torres • NSSpain 2017 • @esttorhe
  37. This 2 sounds like Presenter & View related tasks. The

    Interactor will ask the Data Store for images; it will then notify the Presenter when its done loading the Entities and passes then a «subset» structure; never the Entities up to the Presenter. In this case we'll send the URLs and a title for each Image retrieved from the server; then the Presenter can easily tell the View that it should display the loaded elements IMAGEGRAM 1. Connect to an endpoint (Network) 2. Parse JSON to Entities 3. Load the images in the Entities ✅ 4. Display the images in a grid ✅ 5. Show a detail view 37 — Esteban Torres • NSSpain 2017 • @esttorhe
  38. Last but not least we have to show a detailed

    view; this means a work between the Presenter reacting to the User's request, the Routing creating the next VC and pushing it into the stack and then a different Presenter telling the new view to load a bigger or better resolution version of the desired image IMAGEGRAM 1. Connect to an endpoint (Network) 2. Parse JSON to Entities 3. Load the images in the Entities 4. Display the images in a grid 5. Show a detail view ✅ 38 — Esteban Torres • NSSpain 2017 • @esttorhe
  39. The network layer is pretty trivial and non-important to the

    whole «learning exercise». Let's see this as an implementation detail. We'll just make use of NSURLSession and call an API From what I could get from VIPER is that the Use Case is the heart of it; it will encompass everything that you need to do in order to achieve a «feature». The use case will hold the spec and criteria for your feature. Network INTERACTOR 39 — Esteban Torres • NSSpain 2017 • @esttorhe
  40. So I defined my Interactor's protocol or interface like this;

    nothing too mind blowing and its ok to not read this; we'll come back to it later; but while I was defining my interface I bumped into my first moment of doubt; or questioning SearchImagesInteractor internal protocol SearchImagesInteracting { var output: SearchImagesInteractingOutput? { get set } func retrieveImagesMatching(criteria: String) func loadMoreImages() } 40 — Esteban Torres • NSSpain 2017 • @esttorhe
  41. There's an «important» part of the interface and perhaps in

    a way; of VIPER itself. How do you communicate back to the outer layers when you want to notify that you are done? I guess good old way would be via delegation pattern ! 41 — Esteban Torres • NSSpain 2017 • @esttorhe
  42. According to how it should work; you basically want to

    tell your inner layers to do something, and then wait for them to do so and get the results back later; the way this is achieved (as far as I could tell from the definition and articles) is via defining some sort of «delegate» and then you just let the outer layer know via this «delegate» ALTHOUGH IS NOT REALLY delegation 42 — Esteban Torres • NSSpain 2017 • @esttorhe
  43. So heading back to my interactor definition we can see

    that I defined said delegate as an output for my interactor; and here's how I defined said output SearchImagesInteractor internal protocol SearchImagesInteracting { var output: SearchImagesInteractingOutput? { get set } 43 — Esteban Torres • NSSpain 2017 • @esttorhe
  44. Two important things to notice; 1 is that here Image

    is not a UIImage and is neither an Entity but a pretty simplified struct that will be used to pass information up to the Presenter And the second and to me the most important thing is; do we really need or want to define the «outputs» as delegates? I've had this discussion with some coworkers; somehow the naming feels wrong because it will entail that you are delegating an object to do something rather than just notifying that something is done and more importantly; at least to me, it feels a little bit outdated. Like I said before I'm in no ways a VIPER expert and knew only the basics but given that we have closures now in Swift and even blocks in ObjC wouldn't it make more sense to perhaps have a completion parameter at the end of each method in our Interactor protocol? SearchImagesInteractor internal protocol SearchImagesInteractingOutput: class { func didLoad(images: [Image]) func didFinishLoadingImages(with error: APIError) } internal protocol SearchImagesInteracting { var output: SearchImagesInteractingOutput? { get set } 44 — Esteban Torres • NSSpain 2017 • @esttorhe
  45. Here I over simplified the code to make it fit

    in the slides but the idea is the same. I guess if we do it like this we also get rid of the need for the inner layers to be able to reference the outer layers? Would this make it any less easy to read or understand, or perhaps even test? I don't really have an answer; while I was experimenting with my sample app I did stop for some time to ponder on this; I was not able to find good resources that explain why there's a need for the delegates or outputs vs blocks or closures; perhaps it was all due to timing, when Clean Code was «ported» to iOS perhaps we didn't have this resources available and thus the architecture was defined as it is now and we kept on carrying the same pattern even when new features were made available in ObjC and even with new languages like Swift Also for the sakes of learning I ended up following the good old proven already established pattern and created protocols for my outputs and views but thought it was worth mentioning SearchImagesInteractor internal protocol SearchImagesInteracting { func retrieveImagesMatching(criteria: String, completion: (Result) -> Void) func loadMoreImages(_ completion: (Result) -> Void) } 45 — Esteban Torres • NSSpain 2017 • @esttorhe
  46. Let's wrap up the «code» part; not going to show

    you the full implementation, that's out of the scope of the talk. So let's try to skim over the surface of the other components ! 46 — Esteban Torres • NSSpain 2017 • @esttorhe
  47. I just defined 2 protocols for each component One that

    will define its interface (used this a lot for when I was mocking and writing tests) And the other one to expose a way to communicate up when I was done processing tasks ! protocols 1. Defining the component's interface 2. Defining the component's output Basically a way to communicate the results to the upper layers 47 — Esteban Torres • NSSpain 2017 • @esttorhe
  48. The app is pretty simple and only had 1 Entity

    and 1 sub- struct that was passed from Interactor -> Presenter ! models 48 — Esteban Torres • NSSpain 2017 • @esttorhe
  49. Finally we are getting to the important bits of the

    talk The talk was not supposed to be centered around me explaining how VIPER works or not but what I learned while implementing it. Now I'll try to enumerate the benefits here Benefits 49 — Esteban Torres • NSSpain 2017 • @esttorhe
  50. Because I was focusing so much in defining my interfaces

    and outputs ahead of time I was able to test my components really well. I started defining from the lowest or internal components outwards of the «onion»; which meant that the very first component that I wrote didn't really had any dependencies; but the next component out depended on the component that I just wrote; which had its nice interface already defined and thus, it was super easy to mock. Basically by the time you reach the outer layer you are fairly certain that everything should work separately as expected. Benefits «EASIEST» TDD PROCESS EVER 50 — Esteban Torres • NSSpain 2017 • @esttorhe
  51. Once I was done with my app and I was

    satisfied with what I learned and did I decided to spice things up a little and change some things; changed the API endpoint, the JSON response and then added another feature to save the history of searches so that users could see their past searches. Thanks to how it was structured changing the endpoint and the JSON response required changes in the Data Store layer only; which is the one in charge of turning the JSON into Entities; and everything else stayed the same Benefits EASY TO INTERATE 51 — Esteban Torres • NSSpain 2017 • @esttorhe
  52. Because every component is pretty well defined and does one

    thing and one thing only and even sometimes classes function only as «proxies» you end up having pretty small classes with pretty well defined «pure functions» (another point to easily testable) Benefits SMALL CLASSES 52 — Esteban Torres • NSSpain 2017 • @esttorhe
  53. Now that we have covered the good; let's talk about

    the bad. Obviously this is not a perfect solution so it has to had its drawbacks Problems 53 — Esteban Torres • NSSpain 2017 • @esttorhe
  54. I've read this in many places; there are so many

    layers to VIPER that it may feel overwhelming; and while is true that you don't NEED to implement all of the components for each feature; only the ones that make sense but even doing so you might end up with lots of layers; and to build up on the last of the Benefits; some classes end up being proxies which in the end would make sense to get rid of them but then again; wouldn't that mean «defining a different architecture» in a way? Problems LAYERS 54 — Esteban Torres • NSSpain 2017 • @esttorhe
  55. The first time that I tried VIPER it felt a

    little bit overwhelming due to all the separate pieces. You might feel lost as to where things should go. Most things are pretty straighforward and you know right out of the bat where a function should be but sometimes is hard to figure out. For example, when loading the detail the Presenter will tell the Routing but at this point, should the Presenter ask the Interactor for some sort of ID, etc that identifies the desired Entity; pass that to the Routing and then the Routing will use that ID to pass it to the next Presenter so that the new Presenter can ask its Interactor for the sub-set Entity that should be displayed? Or the subset entity that you pass from the Interactor to the Presenter should already contain all the information needed for this scenario? Problems HARD TO FIGURE WHERE THINGS GO 55 — Esteban Torres • NSSpain 2017 • @esttorhe
  56. I want to tie in a little bit with yesterday's

    talk about architecture; I've been saying but for some time now. There's simply not a fix all architecture I don't think that VIPER is a silver bullet solution for EVERYTHING. It does a lot of nice things and helps you with some stuff. SO… VIPER THEN? 56 — Esteban Torres • NSSpain 2017 • @esttorhe
  57. Gracias! !" 57 — Esteban Torres • NSSpain 2017 •

    @esttorhe
  58. GITHUB | TWITTER @esttorhe ESTEBANTORR.ES 58 — Esteban Torres •

    NSSpain 2017 • @esttorhe