Swift: VIPER Design Pattern (Architecture, 2021, Xcode 12, Swift 5) - iOS Development

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's going on guys welcome back to another swift video in today's video we're going to be learning about the viper architecture pattern in ios apps so this is arguably one of the more complicated patterns but a lot of you guys have requested it so here is a diagram of basically this pattern in a nutshell and it's not the simplest so we've got uh you know this cool acronym viper which is i guess why it makes it cool so it's a view interactor presenter entity and routing we're going to take a look at you know what each of these things do why it's useful to use this pattern and then we're also going to implement an actual solution which is going to have an api call involved to go and get data so you'll see a real world application of this instead of just a bunch of theory which in my opinion can be kind of useless sometimes so if that all sounds good make sure you start by absolutely destroying that like button down below helps out way way way more than you guys probably can imagine subscribe to the channel if you're new and into ios let's get into viper in ios all right we're going to go ahead and get started by opening up xcode and creating a new project here we're going to stick with the app template under ios let's go ahead and give this project a name of viper make sure your language is set to swift your lifecycle ui kit and your interfaces storyboard go ahead and continue i'm going to go ahead and save this onto my desktop and we're going to expand our xcode window before we get started so viper like i mentioned is a architecture pattern that has a view interactor presenter entity and routing so what i'm going to go ahead and do is i'm going to create a new file for each of those things and then we're going to put each of those things in their respective file and talk about them so we actually understand what the heck is going on so let me go ahead and create a folder called viper and bear with me while i create five files so first we have something which will be the view which is actually kind of a bad term for it and i'll explain why momentarily the next part of the acronym is the interactor all right the next one in viper is the presenter so new file new swift file and we're just doing music files we'll fill these in as we go along uh based on whatever class we need but we'll just start with an empty file for the sake of doing it from scratch the next thing which is the simplest part of this whole thing which is the entity which is basically just a model and the last thing which is the i can go to new file the router and the router is common in other patterns as well it's basically the thing that helps you navigate and route throughout your application so let's go ahead a new file and add in a router alright so let's go ahead and talk about each of these one by one but before i do so one other thing that we want to go ahead and do is i'm going to right click on the main.storyboard and we're going to hit delete and move to trash we're not going to use the storyboard we'll set this up programmatically let's also go down into the info.plist and from in here you want to get rid of the main storyboard file reference and then if you open up application scene manifest and if you keep going down into this there's one more place where it references main.storyboard since we'll do all this programmatically so go ahead and highlight it hit backspace and you'll be in good shape so cool let's talk about each of these so the view i'm going to put comments here as we talk about them before building them out is responsible for you guessed it the user interface now the one confusing thing about the term view here is that you can have a view controller be the view as a part of this architecture but we do want a protocol that's going to reference rather you know outline what this view controller object should have on it and the important thing on here is that we need a reference in the view to the presenter so that's the view in a nutshell and when we build it out you'll see that in practice the next thing is this interactor thing so this is an object that we also need a protocol for and the interactor should only have a reference to presenter so i'll say ref to presenter the interactor's job is to more or less go and maybe get data or perform some type of interaction and then when it has that interaction completed go ahead and hand it to the presenter and then the presenter will take care of what to do with it so in here we can also make an api call so i just grabbed a random api endpoint to get a list of some data back so i just pasted that in there so the presenter is actually the glue that ties everything together so of course it's an object well wants a protocol for this as well and the presenter has references to three things so this has a reference to the interactor it also has a reference to the router and then it of course also has in reference to the view itself because the presenter needs to present the view and tell the view what the heck to do so moving along the entity piece like i said is the simplest this is just going to be the model it doesn't have a reference to anything else it's basically all the entities throughout our application otherwise known as models honestly i think they chose the term entity because it's a cooler acronym to say viper and m doesn't work out very well there but lastly router so the concept in viper is there are different modules in your application and a router can route within its own module so the best way that i like to explain this is let's say you have an app with five tabs each tab is perhaps a module so we're just going to go ahead and say here is a router it's going to be let me just say object and it doesn't have a reference to anything else it only has an rcas1 presenter but it is the entry point for our module and this is where the viper architecture starts so this is how we're going to get the entrance view controller in our scene delegate momentarily so let's go ahead and start building all of these out so i'm going to start with the router since it's how we set up our entire pattern so we're going to have a protocol for each of these types of objects so this is going to be a router and i can also go ahead and call this maybe any router which is a common naming convention for protocols so what we're gonna have in here is uh simply a static function and this is going to be you can call it create or even start both are acceptable and what this is going to do is this will return to you any router just like that and basically come down here and we can create an implementation of this so i'm going to go ahead and say class i will go ahead and say user router the reason i said user here is because our api endpoint is actually going to return a list of users so we'll say user router is of type any router and you'll see an error pop up here in a hot second just like that because we need to conform to this guy so we'll go ahead and add in fix just like that and then in here we want to actually go ahead and create all of our components of vipre and return it so since we only have a router at the moment let's just go ahead and create this we'll say it's a user router and then i can return a router just like that so that being done and said let's go ahead and create the other objects and we're going to want to assign the other vip inside of here so view interactor and presenter but let's go ahead and create them before we try to use them so let's do entity which is pretty simple so entity is basically going to be nothing more than a user struct it's going to be codable and we're just going to have a single property in here called name of type string if i can spell things correctly today so there is our user it's our entity nothing too fancy going on in there all right cool presenter so a presenter is once again going to have a protocol this is going to be any presenter and we're going to have a concrete implementation of it so we can say have a class and this can be user presenter and it's going to conform to any presenter just like that now what is any presenter going to have on it so first and foremost it needs to have a reference to an interactor the router and the view so the way we can do that is by saying first and foremost it'll have a router of type any router and this is going to be a gettable and i guess settable as well and it'll be optional the next thing that we're going to want on here is a interactor and we haven't created this yet but you get the idea we're going to create an any interactor if i can spell it there we go and then finally we're going to have another thing on here which will be a view which is going to be any view and once more it's going to be optional and get set and before this continues to yell at me let me go ahead and just stub out those protocols here so here's going to be a protocol for any interactor just like that we're going to have a concrete implementation in here as well so user interactor and i promise once we tie all this together it's easier to understand right now we're just creating a bunch of objects and protocols and honestly that's one of the reasons that this architecture pattern is a little difficult for some folks to understand because it does have a lot of different components so this one is a view so we're going to say this is a protocol for any view just like that or create one concrete implementation here we'll just call it a user view controller of type ui view controller autocomplete is not cooperating because we need to input ui kit import i should say ui kit so i'll go ahead and inherit from uiviewcontroller and make this in any view so let's go back to where we were so we were back i believe in the presenter so in here we're going to have a any view we're going to have a any interactor so let's go ahead and hit command b see if those auto completes go away all right those errors have gone away let's ignore this for a moment so any presenter what else does this presenter need to do so a presenter will also need some functions on it because if you recall our interactor will hold a reference to our presenter as we put in our comments here so our interactor needs to tell the presenter hey some interaction just occurred so we're going to have a single function on here and what we can go ahead and say is interactor did let's see did fetch we can say users and we can say this has a uh let's see with result and the result is going to be a result in the success case we should have an array of users back in the failure case perhaps an error just like that and that's basically all we need so now we'll hit this little error down here and we'll hit fix to stub out all the stuff that we want and xcode didn't do its job shocker so we'll copy and paste that function as well since we're gonna want it and let's see we shouldn't have any more errors in here this guy should go away awesome looking great and now we can do is move on to the next object which is the interactor so the interactor like we said should have a reference to our presenter so we're going to say var presenter in the protocol will be any presenter it's going to once again be optional get set just like that now our interactor also needs to have some contract in terms of what what you know functions we can call on it so we're going to go ahead and say get we'll say get users and no completion handler because the idea here is the interactor will inform the presenter once it's ready so instead of having a call back just go ahead and make it uh take no parameters so we'll go ahead and add that and also add in that get users function here which we'll implement momentarily and that takes care of our interactor finally we'll come into our view and our view should have a reference to our presenter so we're going to go ahead and say presenter is a any presenter all right and on the view we also want two different functions you can do it in one as well but we can go ahead and say perhaps update with users will be a collection of users which is our entities or we can also say update and we can say update with error and listen go ahead and be a string which whatever error that we want to show on our view in our view controller so that is the bare bones of the pattern let's go ahead and hook this up even more so so we can actually start seeing you know this in action so um let's go ahead and step these out here let's stab that out we'll also want to go ahead and override some stuff so i'll say view did load since it's a pretty basic view controller nothing too fancy going on here we'll say super muted load now for our purposes what we're going to do in here is we're just going to have a table view that's going to show a list of users names once we fetch it so i'll also go ahead and create a table on here so we'll say table view is of type ui table view and we're going to create this with the anonymous closure pattern this has nothing to do with viper this is just creating a very very basic vanilla table view if you're not familiar with table views take a look at my other videos on it and maybe viper isn't the best thing to start off with so we'll just come into here and register a vanilla cell i'm not going to do anything too crazy and we'll also go ahead and by default we're going to say the table is hidden and we're going to say true and the reason we're going to do that is because we might not want to show the table in case you know an error does occur we'll go ahead and assign the delegate and data source of the table after we've added it to the view hierarchy with add subview and then we'll want to go ahead and conform to the relevant delegate and protocols for the data source so here is the data source and now it's going to yell at me to bring in those required functions so i'm going to do that down here we're going to go ahead and say number number of rows this one right here i believe that's number of sections number of rows now let's just return 0 for a moment cell for row at index path and let's just go ahead and hard code this so we can get back to the viper stuff and we'll come back to this in a moment so all right we've got our viper pieces here let's go ahead and delete some of this other stuff that xcode gave us so it gave us a view controller by default we already created our own user view controller so we can trash this guy we don't need that anymore we do want to go back to our router though because in here we left those comments so we need to set up uh our actual uh view interactor and presenter so let's go ahead and say our view is gonna be a any view and we're gonna go ahead and say this is a user view controller just like that pretty pretty simple and let's see what else do we want on here so that is our view and if we go into any view well actually before we even assign more stuff on here what we should probably do is let's go ahead and create the other stuff as well so we'll say the presenter is going to be any presenter and this is going to be a user presenter keeping with our naming convention and lastly here we want an interactor which will be in any interactor and we're going to actually go ahead and use the correct thing and actually go ahead and say this is a user interactor alright so looking pretty good what we want to do next is we want to assign these pieces on one another so what i mean by that is if you go to any view you recall that it needs to have a reference to the presenter so here i can say view.presenter is the presenter up above and let's see this is going to yell at me why it's going to yell at me cannot assign to property view is a let constant dust so we'll need to go ahead and make this a var var and a var like that and the next thing we want to assign is the stuff on the interactor so the interactor just has a reference to the presenter looking good and then the presenter has a reference to basically everything so router it'll have a reference to router keep in mind we defined router right up here and the next thing the presenter wants is a reference to the view so view is view and presenter has a reference to the interactor as well so pretty darn simple let's see what else we want on here we also want the router to have a reference to the view so what i can actually go ahead and do here is i can actually say this any router has a view which should be any view and a ui view controller and the reason we want this here let's go ahead and import ui kit is because this is how we're going to tell our app delegate what the entry points of our actual application is so it's important that we have that on here so let's go ahead and just drop that in like so one way that's cleaner that you can assign this is go ahead and type alias this to entry point and here we can say the view is entry point optional you can even call this like entry and now on here you can go ahead and say there is going to be an entry just like that it's optional so we don't have to give a default value but here what we can go ahead and say is we can say router router dot entry can be our view and we can also save view as entry point and it'll downcast it to the appropriate type so now we can actually probably start seeing this stuff on the screen so let's hook it up in our scene delegate so in this first function here this is where you can actually hook up the first view controller of your application i'm going to go ahead and create a router so we're going to go ahead and say router is going to be our end user router user router and there should be a function off of here called start which will give us a router reference we can even say this is our user router just like that and then we want to get the actual controller out of it so we'll say initial vc will be our user router dot entry looking pretty good and then we want to basically set it up with our windows so this up here turns into windows scene this is pretty similar to if you wanted to do this stuff programmatically and here we would need to actually create a window so i'm going to say you window equals a ui window we're going to go ahead and create this with a window scene which is the thing up above now on this window we want to actually set a root view controller which will be our initial vc we want to say window make key and visible and before we do that or we can even do it after it doesn't really matter we just want to keep a reference to it so we'll say self.window equals window so that all said and done go ahead and hit command b and let's see if we have any build errors hopefully we do not i'm also going to go ahead and change our simulator to a 12 pro max and actually before we even run it let's go ahead and do some uh more clean up here so we're going to go ahead and come to our view now our view currently doesn't even have a background color so let me go ahead and add maybe a background color so we can see the stuff is actually appearing and then after that we'll actually hook up our uh our interactor to fetch a number of users i'm also going to override view to layout sub views we'll say super view did layout sub views and here we'll say table view dot frame is view dot bounds just like that so go ahead and hit command r to build and run and let's see if we're seeing our blue screen hopefully we are if not we'll debug it together so it's still white which implies that it's still launching so just bear with the simulator like it loves to be slow every time i do a video but there is our blue screen look at that so cool we've got our router being created and it's handing the view aka the primary controller uh to our window so that's looking good um what else do we want so now on this view we can have the view say you know go ahead and get me the data however it's not really the views job to do that so what we could go ahead and do is do that in the router but instead of even doing that the other thing that we could do is in the presenter it can go ahead and reach out to go and start getting a list of users so the presenter has a reference to the interactor so one thing we could do is we could say in the initializer here we can say interactor and on the interactor we need a call to get users which we have already so we can do that as soon as we instantiate the presenter here so let's go into this get users implementation and let's actually implement fetching users so like i mentioned i've got this url here which gives us a collection of users back just some json so let's go ahead and create a pretty basic api call here so i'll just go ahead and say a task is going to equal a url session dot shared and we're going to do a data task and we want to do it with a url and a completion handler now let's go ahead and create the url up above i'll just do that up here so let's go ahead and say guard let url is a url with a string i'm just going to paste in that particular url string and now in the closure here we can just go ahead and grab the data i'm going to ignore the response and we'll just validate that the error doesn't equal nil or error is nil i should say so we'll say data is data error is nil otherwise you know something went wrong and then finally down here we want to decode this data into our actual entity which is our router or viper pattern so here we can go ahead and say let's uh response is going to basically be uh try json decoder and we're going to go ahead and decode a collection of users from the data and what we can do is once we have this response we can even call them end to t's so it's named a little better hopefully i spelled that correct entities and in the failure case here we also want to notify the presenter that hey something screwed up but because we don't want to just leave that hanging we also of course want to not forget to execute this task by saying task.resume and we also want to capture weak self here so we don't cause a memory leak so in this case i'm going to go ahead and say self and we're going to say self.presenter and this has two functions on it or we have one function rather i can say so this function we're going to say that this finished with a failure and we need to pass in an error in terms of what happened so what i could go ahead and actually do here is pass in any random error or we can even pass in nil if we go and supply it that way so what i'll go ahead and do is we'll come back to the presenter the any presenter protocol definition here and in the results i'll actually go ahead and cheat a little bit and say error optional what you would want to do actually it's not going to let me do that so let's just do the correct way so let's go ahead and actually declare our own errors here we can go ahead and say enum of fetch error of type error and we can go ahead and say case failed i will just go ahead and use that you probably want your own actual specific error codes to handle them more appropriately but here we'll go ahead and say fetch error dot fill defect which is a terrible generic error but that's okay and in this case we can go ahead and do in the failure is we would be catching the error itself so we can just propagate that back up and then keep in mind we want to handle this case as well which is going to be the success case and instead of passing back a failure of course you would pass back a success with your entities so that is the interactor doing its interaction piece in a nutshell let's go to the presenter and handle this jazz all right so we're in the presenter we've got this here so what i can go ahead and do is i can say switch on the results if we go ahead and have gotten our users we can say on our view itself go ahead and update with a collection of users just like that and the failure case we have an error where i'll ignore the actual error itself but i will go ahead and say users you want to update with an error and i'll just say something went wrong just like that so that that will notify our view basically and if we jump into our view now we want to implement those presenter delegated functions so we have these here so update with users we're going to say self.users is going to be users we want to hold an array of users here which will be an array of user models like that empty by defaults and once we have gone ahead and done that we also actually want to do it on the main thread we can say self.users as users we can also say self.tableview.reloaddata just like that and that will update our actual table view i'll also say self.table view is hidden is false like that and then down here we're going to say number of rows will be users.count and let's properly dq a cell so here i'll say let's sell is a table view and we're going to dequeue a reusable cell with an identifier of cell which is what we registered up above for index path i'll go ahead and return this cell here i will say cell.textlabel.txt is going to be the nth user all right dot name so go ahead and hit command b and make sure everything is still building which it looks like it is now let's go ahead and give this a run and let's see if we're getting our table view of users so we definitely see our blue background but we're not seeing our table so let's see what gives i have a hunch that the api call might be failing so let me go ahead and print out in here if the error occurs and we'll also print here dot users let's take a close eye keep a close eye on our console down here so huh it looks like we're not even getting to this point so let's see what's going on with some more print statements so we come into our presenter and we are definitely calling get users our api url endpoint definitely looks correct so let me go ahead and put a print here we're going to say start fetching you could alternatively also use break points of course but we'll go ahead and use this down here so we're not even calling this so let me go ahead and see where we called get users and let's see what the heck is going on there ah the reason actually we're not doing this is because by the time we actually instantiate the presenter before this interactor is actually set so you actually have um two things you can do on this you can actually do a did set and call it directly in here so basically once an instance of the interactor is set or if you hit command shift o you can jump to any file you can go back to any router you can actually call it on the interactor after the interactor has been created so you can actually call it here as well however this isn't great practice because it's not the router's job to kick off interactions it is the presenter's job however so i'll stick to doing it in the did set here so go ahead and give it a run once more and let's see if we get stuff now all right so there is our list of users looking really good so you can see it's actually super fast where you can't even really tell you know that we're getting stuff back it does flash for a half second but that's the viper pattern in a nutshell so one place that i cut a corner that we can wrap up and clean up rather really quickly is in our view we didn't actually implement this update with an error so what i'll go ahead and do in here is um perhaps we want to show maybe a label in the middle of the screen saying you know something went wrong so let me just go ahead and create a basic ui label here and in here we're going to go ahead and say ui label return label i'm going to say label dot text alignment will be centered and label dot is hidden this will also be hidden by default the presenters functions will basically figure out you know which thing we want to actually present and show i would go ahead and say add sub view for the label label dot center is view dot center and we should probably do this in the layout function down here we'll also give the label a frame of a cg rect 0 0 200 and perhaps 50 kind of arbitrary numbers there and in this function here once again we're going to dispatch onto the main thread since the collar comes from a background thread we'll go ahead and say that users is going to be empty in this case we're going to go ahead and make sure the table view is hidden in case it was unhidden at some point or shown i should say and we'll go ahead and say the label hidden will be false so i'm just going to go ahead and screw up the response purposely in the entity so it does actually go ahead and fail so we'll also say we'll say this name has you know some malformed characters we'll add three s's there and if we go ahead and give this a run and let's see we have an error here let's see what got screwed up all right we're saying cell text label users ah because this doesn't exist anymore we'll go ahead and change that we should see that an error comes back and we want to see a label in the middle of our ui so let's see what's going on we've got start fetching and we should be coming to this function here so let me go ahead and print out error to see if we're coming into there so we definitely are something went wrong is definitely printing the one silly thing that i forgot to do is actually assign the text to the label so we'll go ahead and say label.text equals your error itself and then we'll actually see it on the screen just like that so there's viper in the nutshell so before i forget and push this code up let me go ahead and actually fix up that uh that typo that we did intentionally let's do a quick recap because i think i went through this a little quickly um so viper of course stands for view interactor presenter entity and routing let's start at the bottom up so the router is the entry point where your entire flow for a module comes in think of a module as a logical you know part of your application if you have a tab adapt is one tab and everything you route sub of that the router holds on to the entry points and it also is responsible for creating the interactor presenter and the view and all that good stuff the router is often called from you know another router you could have one router call another or when your app launches and scene delegate here we use the router the next thing that we got is the entity which is nothing more than the model it's just an unnecessarily fancy term for a model it's all the entities that make up your data in your app kind of dumb if you ask me in terms of naming your presenter is the most cohesive glue in your application it holds a reference to your router to your interactor and to your view you might be wondering why does it hold a reference to your router and the answer is if you wanted to route to another sub router or something else you could also extend this any router to say go ahead and stop this current router or go ahead and route to a destination you know it's a little subjective of how advanced you want your router to be but that's why the presenter here holds on to it the presenter once the interactor is set on it is responsible to go ahead and tell the interactor to go ahead and perform an interaction and once the interactor com comes backing with data it'll go ahead and call the presenter and the presenter will tell the view to go ahead and update hence being the presenter which brings us to the view here so the view can also be a controller so not to confuse it with just a simple ui view and it too has functions on it that the presenter calls and the view also holds on to the presenter you might be wondering why so for example if you tap on a button on your view and you want to show a ui alert controller aka like a pop-up alert you wouldn't really want to do that here you would want to tell the presenter hey go ahead and present this alert and let's see last step we've got the interactor which a lot of people will call the backbone of your application it performs all the core interactions so if you think of an app like instagram the interactor would be responsible for going and uploading a post or you know fetching new posts for the feed and things like that so it's basically you know the spine of your application and without the interactor going and interacting and setting up entities aka models for you there would be nothing to present so the interactor holds on to the presenter and performs core interactions such as this api call so that's all i've got for you guys today this has been something that's requested for a while it's a little bit of an advanced pattern so i wanted to try to structure this video to be as helpful and useful as possible let me know in the comments down below do you think this pattern's useful is it overkill i don't know i'm pretty indifferent on it definitely don't forget to drop a like down below if you haven't done so already and subscribe to the channel for ios swift and swift ui videos thanks again for watching i'll see you on the next one
Info
Channel: iOS Academy
Views: 18,370
Rating: undefined out of 5
Keywords: swift viper, viper design pattern, ios viper, viper ios, swift 5 viepr, swift architecture, swift mvvm, swiftUI viper, viper swift pattern, swift design pattern, swift architecture pattern, design pattern swift, ios pattern, swift ios pattern, swift tutorial, swiftUI tutorial, swift 5 tutorial, swift tutorial 2021, 2021 swift, swift 5.0, swift 5 2021, 2021 viper, viper architecture pattern, swift mvc, mvvm swiftUI, ios tutorial, tutorial swift, ios, app, swift 5, make
Id: hFLdbWEE3_Y
Channel Id: undefined
Length: 35min 9sec (2109 seconds)
Published: Sun Apr 11 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.