iOS Swift 5: MVC vs MVVM with Dependency Injection, Combine Framework, Decoupling & Generics

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys how's it going and welcome to this video in this video i want to talk to you about the nvvm architecture on ios and how we can structure an app that uses this mvvm architecture and for this video i'm going to code out an app using the mvc architecture first and then i'm going to transform this into the mvvm architecture and maybe we can just discuss about also like what the benefits are using the mvvm architecture and when should you use the mvvm architecture versus an mvc okay because i i believe most of us are very familiar with the nvc architecture and i just want to show you the uh how it distinguishes from each other all right and for this demo project i'm going to build a very simple table view and i'm going to populate this with some json over here so as you can see on my website i'm on this jsonplaceholder website.typecode.com and what this place does is that it provides us a sample json payload all right guys anyway i also want to apologize for the background sound so i hope that you don't mind because uh everyone's awake at this moment at home so you know it's going to be a little bit noisy okay but i hope that um the voice and the tutorial could be will be clear to you and you you'll probably get some value from watching this video today all right so i'm going to use this uh user's endpoint over here slash users so if i'm to click on this notice that this returns to me a json payload so this returns to me an array of 10 items okay so you see over here id is equal to 10 but what we are really interested in is just probably the name of the user as well as the email which we will use to populate the table view all right so if i can show it to you how the uh the app looks like it's going to look something like that all right so okay just just ignore that that gray background is going to look something like that we have the name over here okay so lena graham as well as the email address over here all right so now let's uh build this using the mvc architecture okay so i'm going to open xcode right now and then i'm going to select the single view app and then let's call this um mrpv sorry mvc to mvvmvvm demo app all right something like that and then i'm going to use the storyboard for this example and probably in some subsequent videos i'll probably show you how we can do this uh using swift ui as well all right so let's click on the next button and i think i'm just going to save this in my desktop all right it looks kind of messy but all right now we are here and then what we want to do right now is to just come to the storyboard and we want to change this to um we want to change this to a table view controller instead so let's delete this and let's just bring in a table view controller oops not table view cell table view controller okay and let's not forget to make this an initial view controller over here and then i'm going to use i won't be using a dynamic uh table view cell so because we only have 10 items so i'm going to just uh create a custom table view cell which has the subtitle as well okay so i'm going to change this to table view maybe let's call this users table view controller and ui table view controller let me just copy this come back to the main.storyboard and let's just paste this at the top over here okay let's just assign this hit the enter button okay so right now i want to just create some very basic uh code over here so cell for row let's do um let cell equals to uh ui table view cell okay and then let's select the one that says style and then let's go with subtitle and then for use identifier i think we can just pass in a new and then let's not forget to also return the cell and then for the cell for the text label dot title sorry text label dot text we want to assign this as hater okay just for uh for placeholder sake and then the subtitle is it subtitle sorry detail dot text equals to uh subtitle all right so we're going to do a number of rows and section as well so let's just return 5 for the time bay and let's just quickly run the app to ensure that this is working okay so let's just build this all right okay just let me just run this for a little while okay all right guys so as you can see right now we have this five cells over here header and subtitle okay so now what we want to do is to make a call to this endpoint over here so let me just copy this and then i'm going to maybe just create everything within the same file so we don't have to toggle back and forth so let's create some kind of api manager here api manager okay and then i'm going to do let url url string equals to this oops okay just give me a minute all right so i'm going to create a function over here that says fetch users okay let me just bring this up to the top a little bit and then fetching users is going to return to us some kind of result over here so let's create a completion handler okay so oops so within this i'm going to do a swift result type and then i'm going to pass in something over here but of course let's let's define the model now so let's go to strut let's go with user and then let's come to the payload to see how the pillar looks like alright so we want something like that let's copy this and let me just paste this over here all right so what do we need maybe we do need an id all right let name and maybe let email okay so we need this three properties over here i don't think we need the rest okay and then let's also make this decodable okay so that we can decode this easily without you know passing it manually okay so i i want to pass in the user and array of user and of course in the failure case i want to pass in the arrow over here all right so guys so this is the typical workflow that i i believe most of us are very familiar with so url session dot share dot data task with okay probably this one and then let's construct the url equals the url okay let's find the one that we can construct it via a string let's pass in the uh okay maybe let's just cut this in let's pass in the url string let's uh first unwrap this because we know that this will definitely work and then let's pass in the url over here and then let's hit the enter button so data response and error all right guys so the first thing we want to do is to handle the error first so if uh if let error equals to arrow so if an error exists then we want to call the completion handler and we want to pass in the failure case so let's pass in the error and i think uh let's just put in the escaping keyword otherwise xcode is going to bug us in later all right so i'm going to return if an error exists okay so but if the error does not exist then we want to ensure that we have the data so data else return okay maybe i'm going to do something like a fader error instead fader error data data cannot be found cannot be found all right something like that okay and then so if i have the data i'm going to pass it into the the user the user's array model so i'm going to do you're going to use a do catch block okay oops do catch log and then i'm going to do let users equals to try and then i'm going to do json decoder okay let's initialize this and let's decode so we want this one so the type is going to be a user dot self and then for the data we have the data over here so let's just pass it over here and then once this is successful we want to call the completion handler and just pass in the success case so we can pass in the users over here all right if there's an error and it catches it we want to pass it in as well so let's call the failure case let's pass in the arrow over here at the back all right so guys this should be pretty easy to understand and i i guess this is the most common way that we have been doing this for many many many years all right so let's also not forget to call the resume method over here okay so very simple stuff over here so what i'm going to do is just completing i think let me do a command b to ensure that this is building successfully great okay so guys right now i'm going to do a uh maybe uh users over here and then users empty array and then maybe let's do a date set okay so every time a user has been assigned to this variable we want to call the table view to reload the data okay and then let's call fetch users over here okay private phone fetch users and then you know um you know we have some of us are very apt to are very inclined to make this a singleton so let's let's share equals to api manager okay and then maybe a private okay so sorry this can't be a private so let's chat a private in need okay so uh the way most people do it will be api manager.chat let me do a dodge share oops why is that not happening uh oh sorry static my bad all right dot share dot fetch users hit the enter button result switch the result okay if this is a success case let users and then we're going to assign that set of fuses equals to users all right if there's a failure case let's handle the error maybe for the time being let's just print out the error okay okay guys i guess this is what it will take for it to work okay i forget to assign uh the user over here so let's go with let user equals to users let's pass an index path dot item so for the header it will be user dot name and then for the subtitle you'll be user dot email okay and then for this we need to return users.com all right guys so this is the mvc architecture and i believe this will work will it work okay i think i have to move something to the main thread i believe let me just have a look okay over here my bad alright guys so what i can do is that i can just uh move this dispatch q dot main dot async i can just move this over here instead okay okay let me just build this one more time and i believe this will work all right guys so pretty simple stuff over here so one of the benefits of using the mvc architecture is that as you can see it's really simple to implement you know we we use like you know less than 100 lines to just build this very simple list over here of course you know if you use swift ui is going to be even lesser lines okay but uh but one of the disadvantage of doing this is that it makes testing a little bit harder okay because everything is the logic and the views are very much covered together okay so it's very difficult to do a mocking when writing unit tests all right guys so i want to show it to you how you can do this using the mvvm architecture and also i want to implement the combine framework while uh implementing this using the mvvm architecture so you're gonna learn some extra new stuff over here as well okay all right guys so let's uh get started by creating the mvvm uh architecture for this so very simply rather than just creating a brand new xcode project i'm just going to create a brand new target over here so maybe let's call this single view application and then let's call this um mvvm version all right let's hit the finish button okay so now we have these two targets so what it means is that now i can just toggle between the two different apps over here okay so now i can just open this uh mvvm sorry mvvm version okay and then i'm going to do the same thing as well for the main.storyboard i'm going to change this into a table view controller so let's bring this in table view controller okay and then let's just assign this as the initial view controller and then let's come back to the view controller over here i'm gonna do users users view controller and users table view controller my bad and ui table view controller and let's come back to the storyboard and let's just assign this okay so user table view controller all right guys so the only thing that i want to copy from the previous project is uh is just the the model itself which is this one okay i'm going to change the entire api manager because um we are going to use the combine framework okay so let me just copy this and let me just uh paste this inside over here okay okay so the first thing i want to do is also to just i create those uh those are boilerplate codes over here so let cell equals to ui table view cell let's initialize this dot subtitle nail return cell and then a number of rows in section maybe let's just return five for the time being and cell dot background color equals to yellow okay so let's just make sure that this is working first so let's come over here and then let me just hit the play button so what i do expect to see would be um five yellow cells okay which is cool this is what we see all right guys so now the first thing i want to do is that because we are building this in the mvvm architecture so vm stands for view model so we want to create some kind of view mode over here so let's create class so users view model okay we don't need to inherit anything over here so within this view model this this should contain all the business logic for the user's table view controller so in the mvvm architecture we want to keep the the view control as stateless as possible okay so the view controller should you should think of it as like a ui view all right it shouldn't do anything more than just displaying things it shouldn't be you know making any uh it shouldn't contain any uh business logic ideally as much as possible okay so for this uh view model i'm just gonna call i'm just gonna create one function called fetch users and the difference is that you notice that we're not going to return any kind of completion handler over here so because so once we are able to fetch the user we we want to assign to some kind of variable over here which uh the user's table view controller will observe from okay so guys uh i know it sounds a handful and it's a bit hard to explain so let me just uh build up the app and it will be clear for you to understand okay so what we can do is that we can say hey uh let's create a view model over here so view model oops users view model i mean and then we are going to maybe just set up this view model okay set up view model let's create the private file over here so i'm going to say view model equals to users view model let's initialize this so now we can do that because we don't have any properties over here so but subsequently when we add more things inside here this is going to break and then we can just fix this okay so uh what we can do is that let's create the api manager here so so class api manager okay oops okay and then what i want to do is that i'm i want to import the compile framework because that is what i'm going to use to construct the api manager all right so let me just have a look at my um my my guide okay so what we need to do is that i'm going to create a function over here so class oh sorry fun fetch items all right so notice that the difference is that uh in the previous example i did something like fetch users but over here i'm just going to call this fetch items because i want to make this as generic as possible as dynamic as possible okay so i'm going to also just um add a completion handler over here okay so let's just make this escaping escaping and then because i want this to be generic i'm going to do something like that okay and what this means is that the items that i fetch needs to conform to the decodable protocol okay so i think you'll be very familiar with something like that you know the previous example we did something like this user error but this time what i want to do is that i want to make this t instead of user okay so that this is dynamic all right so what this means is that you know in the previous example you know you'll be very tempted to do something like fetch users blah blah blah over here and then you have another one that says maybe a fetch commands and then you have something like fetch post okay yeah you're very likely to create all this kind of functions over here and then just call this functions in places of the app that you need okay but in this example in this mvvm example we are only going to create one function and we want to make this as dynamic as possible and therefore we bring in this uh this generic over here all right guys so i'm going to show you how we're going to do this so url session we're still going to use the url session okay dot share dot data task but this time we want to use this data task publisher over here okay so i want to pass in the url so maybe i can just pass in the url over here okay so it will be url here and then i'm going to do a dot map oops this is working okay dot map okay and then i i'm only interested in the data okay so let me just remove the space over here and then i also want to decode this all right so let's use this one over here and let me just make this into a new line so that it's a lot easier to read so this decodable protocol all right so again in the previous example we did something like that but this time i'm going to use a t in state okay so for the decoder it's going to be json decoder let's initialize this okay and then we want to call the sync method all right so what the sync method does is that every time we get a result we want to this this thing method will actually be called to uh to pass us this result okay so let's handle the second one over here first so receive value is when there's a success okay so let's hit the enter button so this will be um the result array result array okay so when this happens i can call the completion handler and i can pass in the success case over here and i let me just pass in the result array okay so for this one over here it says receives completion completion.error so this happens this will be caught when we receive an error or when we decide to end the um the the process okay so let me show you what i mean so let me just hit the enter button again and maybe i'm going to call this result completion so let's switch through the result completion you notice that there are two cases over here one is the failure case and the other is the finished case okay so we are only interested in the failure case so let me just handle that right now okay so if this is happening let's call the success sorry the failure case and let's pass in the failing the arrow over here okay so this completion is this guy over here okay but if let's say this is the finished case then maybe what i'm interested to do is just to break this okay all right so this is looking okay let me just do a command b to just build this okay bill succeeded but this itself is not going to work okay because um we have to store this inside some kind of subscribers that's why you're saying that the result of call is unused so what i can do is that i can do something like that private var subscribers subscribers oops am i spelling this incorrectly yeah subscribers equals to set of any cancelable okay and then i'm going to just store this inside the subscriber over here alright guys again i apologize for my birds that are chipping away it's it's the morning right now so the family is a bit active so uh excuse me for that and then let's do a command b so i believe this should work okay so what i'm going to do now is that so when i call this fetch user method i want to uh call this fetch items a function from from this api manager so i'm going to use dependency injection to do this so dependency injection all right so i'm going to do a private let api manager equals to api manager and then i also want to have the uh the um sorry i also want to have the endpoint as well all right i'm gonna steal that again from here all right i think i i needed this oh do i have it oh sorry guys my bad let's let's let's just create this right now over here so let's go with straw maybe let's create an enum over here enum endpoint okay and then let's do with a users fetch okay and then let's do var a url string this will be of type string let's switch the user okay so if this is a user fetch okay let me just remove the decode the the default if this is a user fetch then we want this okay so return this all right guys so uh as you can see i'm what i'm doing is to modularize everything rather than coupling it together so i know that uh the the the downside is gonna introduce a lot more code but this will be a lot easier um you know if your app subs to become very big uh and it's really helpful to just modularize everything okay so i want to pass in an endpoint as well so let's come over here to the top so endpoint so endpoint over here all right okay so what i'm going to do is that i need to create an init over here so i'm going to pass in this api manager and then i want to pass in the endpoint as well okay so you'll be self.api manager equals to api manager self.endpoint equals the endpoint okay so command b to just build this so it's breaking okay so we have to pass in these two things over here so it's completing now so let's let's create an instance of it api manager equals to api managers initialize this and then let's paste this in here and then for the endpoint we are we are going to call the user fetch okay so the reason why we why i created that enum is because you could have you know many other cases so for example uh commands fetch okay something like that whereby you know you have to commands fetch and then probably probably it's gonna look something like that all right all right so you can basically define all your endpoints over here and then just switch through it all right so that's the reason why i do it this way and then under this fetch user i'm going to call api manager dot fetch items okay so let's construct the url so url equals the url and then let's find the one that has string inside which is this guy over here and because i have the endpoint i can do something like endpoint dot url string all right so as you can see this is really really clean uh and it's very reusable in that sense okay so let's pass in the url over here and then let's hit the enter button so result so switch switch through the result and i believe xcode is going to complain okay so okay so as you can see over here this is a underscore and the reason why this is happening is because uh it says that the generic parameter t could not be inferred and the way we can fix this is we can define what the result type signature should look like all right so it should look something like that result and then we know that in the success case we want to return the user on the failure case we want to return just the arrow itself okay and therefore now if i do a command b this will build successfully okay so let's do switch result okay so if this is a success case we get the users okay if this is the failure case obviously we get the the arrow over here as well okay so what i can do is that i can make a variable over here so let's call this um let's call this a user's subject i can do something called uh let me see let me look at my uh my guide for a little bit okay so i can do something called a pass through subject over here pass through subject so what a password subject does is that um at the start it doesn't it doesn't contain any value so as you can see here it doesn't contain any initial value but once we assign this this will emit an event okay so the signature of the pass-through subject will look something like that right so typically you do uh okay we'll do this user and error and let's just initialize this so when this is a success case and we receive that user's model we're going to do users subject dot sent okay and then we can send the users inside over here okay if a failure case happens so uses subject dot send we want to select the one that says this one over here and then let's do dot and then let's pass in the error okay so right now it's going to complain because we're in a closure so uh let's do a weak self okay we can do something like that myself okay i believe this will work uh all right let me do a command b again to ensure that this is building correctly all right guys so this is uh what we need to to to construct the user's view model okay so what i would like to do over here is that let's call fetch users here okay and then maybe let's do it at the top at the bottom over here so far fetch users okay so what it means is that i can just call viewmodel.fetch users over here okay and because i am fetching this i'm calling this fetch user method once this is once the result is being written to us this uh this pass through subject will be updated okay so what we want to do is that we want to bind that to the user's table view controller so maybe what i like to do is to call observe a view model okay so let's create a function over here so fun private fun observe view model so i'm going to do a view model dot okay so i have the user subject over here and then i can call sync all right so again hit the enter button this will be um result completion and then this will be uh users okay so what i can do is um okay let's create uh a user's array at the top over here so user equals to something like that all right all right so now that we have the users we can do um users.com and then over here i can say cell dot title sorry dot text label dot text equals to let's create the user first equals to users index path dot item user dot oops am i getting something over here no i'm not what's the problem let's see oh like that silly me all right so user dot uh name and then cell dot detail label dot text equals to user.email okay guys so once i have the users i'm gonna assign self.users equals to users okay if an error exists then maybe what i want to do is let me have a look all right so i'm gonna just switch through this switch result completion case if an error exists then maybe i just want to print out the arrow over here okay and then uh default let's just break this oops okay and also i have to create some kind of subscriber over here so private var subscriber okay so i can maybe just create an uh any cancelable like that over here so this is the other way to do it okay so i can just assign it like that okay so i believe this is going to work okay so i can also just call self.tableview.reload data over here as well okay and maybe it's best for us to just move this into the dispatch queue dot async let's move this into the main thread so that we don't get any threading issues over here all right so i'm gonna quickly just run this and let's see if this is working for us all right guys so as you can tell the um this uh table view is loading as well so if i'm to break put a breakpoint over here and let's just run this one more time i should be able to land over here so if i do a po po users you should see that this displays all the users within the the json payload itself all right guys so let's let's just talk about what is
Info
Channel: Kelvin Fok
Views: 5,338
Rating: undefined out of 5
Keywords: mvc vs mvvm, dependency injection, combine framework, decoupling, swift generics, ios tutorial
Id: jbukBpv2PaY
Channel Id: undefined
Length: 29min 20sec (1760 seconds)
Published: Sat Aug 08 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.