MVVM + Dependency Injection in Swift | Unit Testing | iOS

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys welcome to my channel i code i am pallav and today we are going to see mvvm with dependency injection i know that there are hundreds of videos on youtube on mvpm and you might be thinking that how this one is going to be different so let me tell you that in this video we will be spending very less amount of time in theory rather we will move our focus to the implementation part we will see the points where people get confused will see good practices bad practices how can we implement dependency injection along with the mpvm that is the whole point of it we will see that what are the different ways of implementing mvpm how can we write test cases how dependence injection helps how can we make our code loosely coupled and everything else about mvvm so let's get started so mvvm is an architecture that has become very popular in last few years and this is because the way it keeps the code loosely coupled it makes it testable so in interviews when generally candidates tell that they have worked on mpvm i feel really good and then i asked that why did they choose mbvm and the general answer is that to keep the code loosely coupled to keep the business logic separate awesome that's the whole point of mvpm and then i ask them to write a small piece of code in which they can demonstrate that how mvpm is helping in writing the testable code how are they doing test driven development using mvvm i asked them to write a view model and a test case for that view model and the struggle begins it feels like a real challenge a difficult one so the whole point is that most of us are comfortable with the theory part of mbvm that there should be a view model there should be a view they should be separate but when it comes to actually implementing it when it comes about the good practices the best practices writing the test cases most of us fail we'll fix that today but before jumping onto the code let's quickly wrap up the theory so in mvvm there are three entities that is view model and view model now view is the entity which takes care of all the ui stuff all your ui components should be there in the view and that's it you should not be having anything else apart from the ui components its only responsibility is to take care of the user interaction about the ui components and to update itself based on the actions performed by the user that's it the second entity is model models are generally dumb what i mean is that we do not write logic in the models so models are there objects which we get from our databases be it rail be it code data or maybe we get them from the web services but they are just the raw structure the raw object and that's it no logic associated with them and then comes the third entity that is view model this is the most important entity in three of them from understanding point of view of course the other two are also important but from understanding point of view viewmodel is something that we will be focusing on and viewmodel's responsibility is to interact both with view and model so whenever the user interaction happens view is supposed to interact with viewmodel and to convey that user has performed some action depending on that action viewmodel knows about the business logic that what is to be done some web service call is to be made or some computation is to be done some logic is to be executed and depending on those viewmodel performs that action and responds back to the view so this is how it is done view tells viewmodel that user has performed some action and based on that viewmodel tells the view that how it should update itself so let's say that user has pulled the table view and pull to refresh was implemented so view will tell view model that user has pulled the table view and then view model knows that a web service call is to be made so view model will interact with the network layer network layer will make the web service call the models will be updated with the new data and when the models will be updated view model will let the view know that ok new data is available update yourself the interaction with model is also similar so whenever model updates view model is informed about that and then view model tells view to update itself that is how the whole cycle works of mpvm so we have view we have model and then we have view model and this is how they interact notice that there is no direct interaction between view and the model the only communication between them happens through view model now let's see that what should we keep in our mind while writing the view model so when we write view models we should take care of these things and the first and very important thing is that we should not import ui kit or s50ui in our view models because when we say that view and view models are to separate entity then there is absolutely no point of having any ui component in viewmodel if you are having it then your logic is flawed ideally you should not have it so make sure that while you write your view model there should be no piece of code which depends on ui component or which will make you import swift ui or ui kit there should be no ui related framework imported in the view model that's the first point and treat it as a thumb rule the second point is that how will your view model communicate with the view so your view will be having the object of view model but not the other way route so we need to figure out some way through which view model can communicate back to the view now there are different ways for this and i have seen people getting confused with them so if i am using protocol and delegate for communicating back and my colleague is using combine for communicating back to the view doesn't mean that one of us is not using mbvm we both are using mbbm the difference is only in the way we are interacting with the view believe me i have seen people arguing over this they feel that one of us is wrong well that's not the case it's about the personal preference it's about the project structure it's about the logic involved and there are multiple factors for deciding that but you cannot say that if i am using the protocol and delegate way or if someone is using frp or if someone is using any third way then they are wrong or i am right well these are all the ways to give callbacks back to the view and none of them is wrong because the mvpm architecture does not talk about how part of the interaction it just says that there should be two entities that is view and view model and both of them should be separate there should be an interaction between them but the entity should be separate it does not tell that how they should communicate it can be protocol or delegate it can be frps it can be kvo or anything else so while writing view models you should keep it in mind that how you will communicate with the view the third point is testability well that's the whole point of using mvvm otherwise mvc was also fine we were working with that massive view controller and things were fine but because we wanted to make our code testable that's why we came to mvvm so while writing your view model make sure that you write it in a way that it can be tested later what i mean is that we should be thinking of dependency injection while writing our view models we'll see it and the fourth point is that our view models should only contain the business logic and nothing else so by this point i mean that when you see the code coverage of your view model it should be hundred percent there should be nothing apart from the business logic that your view model should deal with even if you are having the logics for passing date to a string string to date or those kind of things write separate classes for them write helper classes utility methods for it but do not put them inside the view model and do not pollute it so your view model should only contain business logic and that's it nothing else now let's see the second point in little more detail that how will your view model communicate back with the view your view will be having the object of view model through which it will communicate with the view model basically it will tell that what action has been performed by the user and then view model will execute the relevant logic but when we talk about the callbacks that how will view model inform the view about the applications we have certain options for it so either we can use the protocol and the delegate way the second option is using the closures so view will set some closures inside the view model and then when particular closure will be executed it will get the callback the third approach can be using the property observers or some people call it the boxing technique so if you don't know it we are going to see it in a while and the fourth way is using frp that is functional reactive programming so either you can use third party frameworks like rx for it or you can use the apple's framework for frp that is combined so this was about the theory part of mvvm and now let's see it in action let's see an application that is using mbvm and dependency injection and let's understand its code i am having this project which is using mbvm and dependency injection i have also used this in other videos of mine and i got the messages to explain the code of it so i thought that it can be a good use case i am using swift ui for the ui part combined for making the web service calls and the referp stuff for getting the callbacks from the view model and all that and nothing for the dependency injection i haven't used any third party library for dependency injection so we'll understand the dependency injection by writing the code from scratch no third part is involved so this is the application let's understand the use case first and then we'll understand the code so this is the home screen which makes a web service call and fetches a list of flight those flights are displayed here in the list and on click of any particular flight we have a detail screen another web service call is made from this screen to fetch the details of that particular flight and those details are rendered here so you can see that we have some stoppage here some duration uh and an original price was there and then we are having a discounted price whether the flight is cancellable or not so i'm pointing out these things specifically because this is where the logic is involved this is where i have used the mvvm and the dependency injection part the others are the static text which i am not concerned about so specifically for this price component this discount part will be using dependency injection for these things like flight is concealable or not or how many stops it is having we will be writing test cases for it while others are the static text that i am not concerned about so let's not discuss about them so this will be our use case through which will understand the mvvm implementation and the dependency injection now let's start with the code so the first thing that i would like to explain here is the directory structure i have seen those kind of directory structures where people make three directories they view view model and model at the root level and inside each of those directory they create the sub directories for the modules so let's say the view directory will be having the sub directories of home and detail and view model will be again having the home and detail and model directory will be again having the directory for home and detail well it can be a personal preference but i do not like it because after a time it is not scalable if you create your directory structure in such a way that that your root directories are view model and view model and inside that you are creating the sub directories for the module then after a time it will get really difficult for you to search for the files and to to explore the things so kind of directory structure that i prefer is that i have the directories for the modules at the root level and then view view model inside them so here i'm having the directory for home and detail and each of them are having the sub directories as model view and view model and and it makes the the working it makes the development really easy after this so here we have home and detail as two modules which are having view view model and model and then we are having a directory for manager where i'm having a network manager which will be using combine and then some other utils for for having the utility methods as i mentioned earlier in this video so this is about the directory structure of this project and now let's look at the code so this is how my home view looks like i'm not explaining the code for the view because the video is not about swifty or making the view i just want to mention here that i am having a view model here i'm having the reference of my view model and this is my home view model we'll see this in detail but for now i'm just telling that i'm having a view here and then i'm having a view model the view is having the reference to my view model and then i have a model for flight so this is how my model looks like it is absolutely dumb no logic involved it's just a struct having the properties for flight details and it is confirming to certain protocols for decodable and hashable and those kind of things now in this view i'm having the object for my home view model so when this view will load it will tell home view model to fetch the list of flights now regardless of the views implementation that whether it has been implemented using swift ui or ui kit you are having the list or you are having the table view it does not matter at all all you need to do is just just tell home view model call some specific method of it to fetch the list of flights or to fetch the data that whatever you want and then the entire responsibility comes to view model so let's not focus on the view part you can use ui kit and it will work in the same way so here in the view model i am having this method for getting the home data that is get home data which is using network manager that is my network layer to get the data for the flights and because my network layer is also using combine for making the service calls that's why i am having this sync and receive value and store and all these stuff here so if you are not comfortable with combine or you are not understanding this code then here is the link to the video that i did on combine where i explained everything where i explained that what is this thing this receive value is stored how combined works what are the different publishers subscriber and all those stuff so you can have a look at it but it is not very specific to mvvm you can either use combine or you can use the traditional ways for making the web service calls and whatever ways you are comfortable with if you if you prefer getting the callbacks to protocol and delegate that is also fine so at the end of the day you should be getting the data after making the web service call in your view model that's the whole point here so this method that is get home data its only responsibility is to tell network layer to make the web service called to fetch the data and get it back now when this method will be having the data for flights back after receiving the the response of the web service call how it will communicate back to the view that is our point that we will be discussing so for that i am using combine here now it's not like that i'm a big fan of combine that i'm using it everywhere or something like that it's just that i follow practice that start using the things with which you are not very comfortable so instead of running away from those things that i don't know it i don't know combine or anything in particular just start using it so here i'm using combine for giving the callback to the view but what are the other options that we discussed so one was protocol and delegate so how you could have done it is you could have made a protocol say home view model delegate and this should be any object and it should be any object because i want the delegate to be weak and whenever we want something as weak it should be reference type so that's why i have used any object here and keep it in mind that this is one of the interview questions that what is the difference between any and any object where most of the candidates get confused so this is the thing that when we want something as the as the reference type we use any object for it so here i'm using any object for this protocol of mine that is home view model delegate and then we can have any method in this let's say uh funk did did receive home data maybe and then we can pass on flights so this will be an array of flights and here in the home view model we can have a week where delegate that will be of type home view model delegate and and my view will set the delegate itself and when i need to give callback to the view what i can do here is that i can simply ipad delegate dot did receive home data and then i can simply pass the object for flights data or or something like that so you can have different methods in your protocol depending on your requirements so this is one of the ways through which you can communicate with your view and it is completely fine it is i mean it's not a bad practice or or a wrong way to communicate in any way so this is one of the ways the the second way that we talked about was property observers or the boxing technique so i'll tell you that that what it means so here i'm having this class box and i took this code from the windows tutorial so in his tutorial he is using the boxing technique for communicating with the view it's a good article i'll put the link in the description you can have a look at it if you want to see that how boxing technique can be used so in this box what he is having is that he is having a closure which for which he has used the type alias listener and then he is having a property as listener the second property that he is having is value so he has used generics here so that it can be used for any type so that's why the generics are there and when this value will be set it will be associated with the listener so here's the init method and whenever the value will be passed the value will be set the date set will be called and then it will be associated with the listener and then there's this method that is the bind function so this will bind the listener with that particular property with that particular property which you which you need to observe that whenever the property will change you should get some callbacks so for that the bind is used and this is how we can use it so in your view let's say that in your view did load method what you can do is that you can say that viewmodels.flights.bind and to do this you should be having the flights as the box object in your view model so in your view model you can declare the flights as box and then and then pass an an empty flights array may be just for initialization and then the view did load in your view you can just call the the bind method so that you will receive the callbacks whenever the flights property will change so this is the boxing technique essentially it is also following the observer pattern that we are observing to some particular property which we have wrapped in a class box and because we we do not want to import a third-party framework like rx or we do not want to use combine for just this this minor purpose that's why this boxing technique has been used but other than that you can always use the the combined framework or any frp framework for that matter the third technique is closure so so in closure what you can do is if you want to use closure in your in your view models you can declare the closures here so let's say where did it receive data and then we can have an array of flights and this will be returning void so this is how we can have a closure and then whenever you receive the data you just call this closure and pass the data and because your view will be having the implementation for this closure it will receive the data and then it can update itself so this is how you can use clojure for giving the callbacks to the view we already saw that how we can use the property observers or the the boxing technique and we are already having this implementation for for the combine here the fourth wave was protocol and delegate that also we saw so if you are using combine your view model can confirm to the observable object protocol and then for all the properties whom you want to listen in your view you can declare them with that published and in the in your view you will be having the view model as as your observed object and then it will start listening to all the changes which are happening in that particular object so for more details on combine you can you can have a look at that video of mine which i did on combined so this is how i'm communicating between view and view model and now comes the core part that we talked about that our view model should be only having the business logic and it should be testable so for that let's have a look at our detail view model that what all we are having there so in this flight detail view model i am having the methods for getting the actual fare for getting the discounted fare for getting cancellation tags for getting the stock page and all those and you can see that the code coverage of my view model is really good i am having the test cases for for almost all of the methods of my view model except this one that is get flights data so i initially left the test cases i initially skipped the test case for this particular method because it is involving the web service call and in order to cover that i i had to create the stubs for getting the mock response and all that that i didn't want to cover in this particular video as it would have been you know core on the unit testing part so i skipped that part so that we do not deviate from our topic other than that all the methods are are having the test cases for them the code coverage is really good and we'll see that how writing of the test cases was possible how the testing was was really easy i mean how did the code was testable though it is having the external dependency on one of the components that is the the discount eligibility manager so let's see that what it is and how the dependency has been resolved so here if you see in my detail screen i'm having this price that is three two eight eight seven now this is the actual fare that i am receiving from the web service from the response and then there's this discounted fare that is two eight 28611 so this is the discount fair and based on some percentage of discount that i am receiving in the web service response i am applying that discount and that is how this discounted fare is is coming here so i am having this method here that is get discounted fare now it takes the fare the actual fare from the from the details object and then i get the discount percentage from the details object and then i'm having this entity that is discount eligibility calculator which is having a method to get discount eligibility now this method checks that whether the user is eligible for getting the discount or not so for that this method can have certain web service calls in it so if we see this method that is get discount eligibility there can be a bunch of stuff in there so the very first thing that it will do is that it will check that whether user is logged in or not if user is logged in then it will get the reference of the user's object to get the necessary details of the user and then probably it can make a web service call for for for getting the eligibility so generally these logics are implemented at the server side so it will it can make the web service call it will pass the user data along with it after getting the response it will pass the data and then there can be some additional logic based on the data that has been received through that web service call so what i mean is that there can be a bunch of stuff that can be involved in this method and i just left it blank for the demo purpose but in the actual project you will be having certain dependencies on which your view model will be depending on and now let's see that how this dependency has been resolved using dependency injection so if we see at this method that is get discount eligibility which is being called from the reference of discount eligibility calculator so this method is in the class that is flights discount eligibility calculator and this class is confirming to a protocol that is discount eligibility calculator so if i see this protocol i'm having this method that is get discount eligibility now what was the need of of creating this protocol i mean what i could have done is that i could have simply instead of taking the object of this this protocol type uh what i could have done is that i could have simply taken say where discount eligibility calculator and then say flights discount eligibility calculator something like this and then also this implementation would not have changed because get discount eligibility is there in flights discount eligibility calculator class also so this implementation would not have changed so then what was the need of creating the protocol well this is where the the dependency injection comes in because this view model of mine was dependent on a third entity that is flights discount eligibility calculator so what i did is that i created a protocol and removed that dependency so now what i'm doing is that i am injecting that dependency through the initializer and the point is that how will this help so this will help me in writing the test cases so when i will go for writing the test case for this particular method that is get discounted fair i do not need the object of actual flights discount eligibility calculator instead i will have the protocol of a dummy class that will be confirming to this protocol and then i can pass the mock data so because i wanted to be able to pass the mock data i use this protocol and then injected the dependency through the initializer this is how the dependency injection works and that has been done here as well so i'll just revert these changes and i'll show you that how the the test cases are working using this so if you see that this flight detail view model it is having only business logic nothing else this method is responsible for for making the web service call and getting the data after that we are having these methods for get actual fare get discounted fare so these are all the business logic that i wanted to compute the discount percentage and all that so that is all related to my business logic so apart from my business logic i'm not having anything else in my view model now let's see the test cases that how test cases have been written so i am having this sut object what suv stands for is system under test so whatever system that you want to test is generally taken as the sat object and here i wanted to test my flight detail view model so i am having this flight detail b model and as we saw that the initializer of flight detail view model takes an object of discount eligibility calculator so what i did here is that i created a class that is dummy eligibility calculator and confirm to the protocol discount eligibility calculator inside that i confirm to to the method because i am confirming to this protocol i need to implement this method that is get discount eligibility and instead of making any web service call instead of any computation logic or anything else i simply returned true from here or i could have written false depending on my case so i returned true here and now my view model is ready for testing now i am having the different test methods for testing the discount calculation testing the the cancellation string and all that so if we see at this method that is test discount calculation we are passing mock data here for testing or at times we are not passing a data at all to test the failure cases that how those cases are being handled so the crux is that without the involvement of actual discount eligibility calculator or without the involvement of this entity that was flights discount eligibility calculator which was having the the logic for the web service calling and everything we are able to test our view model because our view model is having the dependencies injected from outside and it is not having inside them so if i repeat it again just to make it clear what we could have done is that we could have taken the the object of flights discount eligibility calculator inside this view model itself but then the testing would not have been possible that's why we created a protocol we made this entity confirmed to this protocol and then took the object of the protocol type instead of taking the object of the concrete type itself so this is how the dependency injection works and because of this the code becomes testable so if we go to the test cases and if i just run the test cases let's see and you see that i can i can execute all the test cases and if you go for the code coverage here in the flights detail view model which we are concerned about so it is having a code coverage of 72.3 percent and the remaining is because of that method which i left here that is get flight details because it involved the system and the mocking of the the web service call so i left it initially but other than that we are having the code coverage for all the methods so that is how you can implement mvvm and that is how you can use the dependency injection the dependency injection makes it testable it makes it loosely coupled and this is what you should keep it in mind so while writing your view model you should keep it in mind that how you will test it whether this implementation is testable or not if there are any dependencies are you having any provision for passing the mock data are you having the protocols for them are you injecting them from outside be it from initializer be it from some method so that is a different thing and if you want to learn more on the dependency injection here's the link of a video that i did on dependence injection so this is how you should implement the the view models and that is pretty much about the mvvm with dependency injection if you are having any doubts please leave them in the comments i'll try to answer as much as possible and as far as the code is concerned i'll put this on the github and we'll put the link of the repo in the description so you can clone it from there that's pretty much for this video a new video comes out every weekend so you can consider subscribing to the channel let's write better code together happy coding and stay safe you
Info
Channel: iCode
Views: 5,090
Rating: undefined out of 5
Keywords: iOS, swift, xcode, iCode, pallav, pallav trivedi, android, mobile app development, iOS interview question, architecture, mvvm, di, dependency injection, unit testing, design pattern, sample project, combine, protocol, delegate, frp, rx, box, property observer, app architecture, mvc, viper
Id: Q1sjLDQMNzU
Channel Id: undefined
Length: 29min 28sec (1768 seconds)
Published: Sun Sep 12 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.