Core Data MVVM in SwiftUI App Using NSFetchedResultsController

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone and welcome to athenshire weekly in this video i'm going to show you that how you can use mvvm design pattern with a core data type written in civ ui and we will be using the ns fetch results controller so we don't have to call functions on core data to refresh it will automatically refresh all right so let's go ahead and get started the first thing we need to do is we need to add our core data model so let's go ahead and search for model over here or data and we can create our particular model i can call this a budget app model currently the model doesn't really have any entities so it's not going to save anything let's go ahead and add an entity and i will go ahead and say this entity is called budget it will have number of attributes like title that can be a string and also total which can be of type double and we will also make sure that these are not optional this means they are required all right so that's it so we have created our data model which is our core data model which will allow us to save a budget entity and by default it's going to save it to seo light database it will have a property called total and a title the next thing we want to do is we want to make sure that we are initializing core data so i'm going to create a folder called managers and inside that managers i'm going to go ahead and create a file called core data manager and we have written this file number of times so let's go ahead and see how we can write that import core data class core data manager the purpose of code data manager is to initialize the core data stack and in order to initialize the code data stack we will use the persistent store container and that will be a persistent store container and we will return you the same exact object of core data manager so we're going to create a singleton so here we go code data manager in order to create the singleton we need to make sure that nobody can create an instance so i'm going to go ahead and do it like this basically creating a private initializer and inside the initializer i'm going to go ahead and initialize the ns per system container passing in the name of the model that we just created which is called budget app model so you can see this is the same as this next up we're going to use persistent store container dot load persistence store we will get the description we will get the error if there is any error if there is any error then well everything is failed so we can't really do anything we can simply say unable to initialize core data and kind of like display the error all right so this will be all the code that you need to initialize the code data stack the next thing we want to do is we want to inject the view context into our environment so that we can access it inside our views so i'll go to the budget app and we will go ahead and create an instance well first of all we will get access to the view context we can use our code data manager the one we just created and this will give us a view context and finally we can go ahead and inject that view context by saying environment and manage object context and the view context this means that the view context will now be available to the content view and it will also be available to all the other child views children's of content view all right all right let's go back to the content view the first thing you will note about this content view is well we're not displaying anything inside the content view right now and the content view is kind of like displayed on the right hand side in xcode preview so that's fine i mean we can access the manage object context using our environment variable so i will simply go ahead and say manage object context and now i can use environment variable the other thing that we want to do is to create the viewmodel associated with our content view so the view model that we're going to create for content view i'm going to put it inside a different folder and i will say view models and when i'm creating a view model i'm going to go ahead and create a new file and i will call it budget list view model so this will be my view model which is going to be working with the budget list meaning it is responsible for providing the data to the content view to display all the different kind of budgets that i have or be like whenever i create those things okay so now we have the content view we have the view context somehow we need to give this view context that we have created to the budget list view model all right so in order to create the content view what we can do is we can make sure that when we are creating the content view in order to create the contribution we need to pass in a budget list view model and in order to create a budget list view model we can make sure that you pass in the manage object contacts because the budget list view model will have the functions like save and delete and all that stuff that we will require right so how do we do that then well the first thing i'm going to do is inside the budget list view model i'm going to create a property which is called context this will be a ns manage object context and now i can go ahead and create the initializer so ns manager object context i can go ahead and assign the context and i can call super dot init for now but we don't really need a super donated for now but since we just want to create an instance of budget list view model so this means that in order to create a budget list view model we need to pass in the context so let's go back to the content view and inside the content view you can see that we are not creating the budget list view model all right so now the question becomes okay in order to create the content view we need to pass in a budget list view model so view model budget list view model and we will have to create a property over here private var budget list view model which is budget list view model and then we can say budget list view model equals to vm all right now obviously it's complaining over here because when we are calling content view we need to pass in the budget list view model which we are not so let's see what we can how can we fix this problem so now i can say budget listviewmodel but now i require to send a context and i can go ahead and probably access the context from my core data manager so codatamanager.share.persistentstore.view and then finally pass in the viewcontext now this is now going to complain because in order to create the content view now you need to pass in the budget listview model so if you go over here into the app it is complaining that you created the content view but you never pass in the view model so let's go ahead and pass it perfect and this is now much better because in order to create the content view now we are passing in the budget listview model and in order to create the budget listview model we are passing the view context so the budget list view model will have access to the context which is great now let's work on adding something before we do anything else because we need to make sure that we are adding a particular budget and in order to do that we will go ahead and create a v stack right now we're not really displaying the budget so that's fine if we're just displaying the text called budgets that's perfectly okay we can go ahead and start with a toolbar all right and let's see if the toolbar actually appears we may have to use like a navigation view navigation view there we go and we will also go ahead and say navigation title and we will say budgets there we go so we have a navigation title i also do want to use a toolbar so let's go ahead and create a toolbar toolbar item and we will have the placement which will be kind of like the trailing placement of a button so we can create a button and we can say add new budget hopefully it will appear there we go let's save it and there we go so we have our add new budget appearing now whenever we press on add new budget we need to open up a new sheet that will take us to a different page all right so in order to open up a sheet i'm going to go ahead and create is presented and whenever you click a button i'm just going to go ahead and say it's presented equals to true okay that's fine but we still need to make sure that the sheet is getting displayed so sheet is presented dismiss we're not really going to do anything this is just for dismiss and whatever the content you want to show which over here will be add budget screen which we don't have and in order to create ad budgets screen we will pass in the add budget view model and we will also pass in the view context to that view model so a couple of different things we need to create we need to create add budget screen we need to create a budget view model let's go ahead and start with the view model add budget view model so add budget judge view model will be responsible for whatever is happening on the add budget screen so add budget view model which is observable object and once again we are going to make sure that you pass in the contacts because we will be needing that and in order to create an instance of add budget view model you need to pass in the context there we go now we also need to make sure that we are creating the add budget screen add budget screen perfect the ad budget screen looks fine one of the things that we are going to do is you are going to be passing in the add budget view model to create that so in order to create the ad budget screen you need to pass in the add budget view model view model add budget view model self.viewmodel equals to vm and now we need to also satisfy the condition over here to create or to pass the viewmodel in order to create the view model we must pass the view context so we have to do all of that stuff over here again add budget review model context view context okay uh let's go ahead and run our application and we will see that if it is able to show us the add screen or not that's the first thing we need to find out that are we able to add a new budget or not so i'm going to click on tap on add new budget it does show me the screen so that is all fine okay so it looks like it's working okay now we need to actually work on the add budget screen all right so before doing that let's go to the ad budget view model and create all the different things that we need so what is add budget view model capturing well it's going to capture the name of the budget and the total so that's why we have that now let's go ahead and go to the add budget screen and we will make sure that the add budget screen can display all of that information meaning add budget screen will have some sort of a capability for asking you for the budget so basically we need forms we need a text field i will say enter the title of the budget the name of the budget or the title of the budget and the text that's going to go into vm.name and there is nothing over here now you can see that it's not recognizing what is dollar sign vm that's because when we created the instance of add budget view model we did not decorate it with this state object now if you go ahead and decorate this with the state object then it's going to complain that you can't do that because vm is get only property so you will have to use over here observed object and observe object can be assigned so then it will work the same thing we need to do for the total so let's go ahead and do the total and finally we will have a save button to save this particular budget let's go ahead and refresh our ui so that it can display something and we can also go ahead and say navigation title add new budget so it will look nice let's see the title well it's going to appear if i go ahead and probably wrap this around with navigation view so i'm going to go ahead and wrap this around with the navigation view so i get the navigation bar or something on the top there we go the same button it might be a good idea to center it uh i can create an extension to do that so let's go ahead and create a new group i will call it extensions there are multiple ways that you can center your button but it will be a good idea to create some sort of a helper function that will allow you to do that so view plus extensions and it will be a very very simple extension the whole point of extension is to simply make sure that your button or any kind of a view is centered and one of the ways of centering anything is using the horizontal stack so i will simply call this center horizontally and this is going to return me some sort of a view i will simply go ahead and use a etch stack i will render cells which can be any view so i'm just going to put this inside or between the spacers so that it centers itself and now i can simply say center horizontally and it should center as you can see great okay so this is great but the save button doesn't really do anything right now so we need to make sure that we are saving it i can go ahead and probably i can go ahead and create an instance of the budget passing in the context which i should have somewhere kind of weird that i don't have that context over here so we need to make sure that our we are passing in the initializer to create that context so let's first go ahead and create the context all right so currently we are on the well we're not going to create this over here i think i'm doing it wrong over here if we are trying to save we will call the save function on the add object or add budget view model so i'm just going to go ahead and say over here viewmodel.save now there is no such thing as viewmodel.save where view model is the add budget view model so i can always go to the add budget view model and create that particular function so let's go ahead and create a function called save which is going to save our data to the persistent storage we will create an instance of the budget we already have access to the contacts so we can simply pass that context over here we can go ahead and say title equals to name and then we can say budget dot total equals to double and total or else we will simply say zero and now we can go ahead and call say but there is no function called save now i can go ahead and say context dot save and that is going to work but it would be nice if we are just able to save like this i mean this is i think much nicer right if we do that so let me show you how you can do all of these kind of things so there is no save function on the manage object budget in order to create the save function we can always perform an extension on the budget so that's one way of doing things so another thing that we can do is also create a base model so let me go ahead and first create an extension and you create an extension so that you can add more features to your budget manage object so i'm just going to go ahead and create a budget plus extensions import core data extension on the budget so we are extending this class that was generated by core data and it's going to be using base model now what is this base model where is this coming from again good question let's go ahead and create the base model the base model is going to provide all the default features to all the different manage object contacts or not many of the context but basically the manage objects so i'm just going to go ahead and create a base model so all the common functions that means uh saving and deleting we can create this particular protocol base model which can provide all of those different things i'm just going to go ahead and create base model and base model will also have access to the view context so let's go ahead and do that and it's manage object contacts get base model will also be able to provide save function and also be able to provide the delete function now one thing that we're going to do is we are going to provide some sort of a default implementations for the save and the delete and also for the view context so static var view context ns manage object context and we can simply return it from the core data manager code data manager dot shared persistent done what about the save function well for the save function we can simply go ahead and say self which is the static instance the class instance and then save perfect but what about the delete function because the delete function is a little bit different in order to delete something we need to pass in the instance now i can't really pass over here self because that will be the protocol itself so i need to basically say that i can pass itself which can be the instance that is conforming to base model but i can also limit it by saying that the self in this case would be ns manage object so that we know that self will always be or the person who is conforming to base model will always be of type ns manage object and that is something that we can use great okay so now we can go back to our budget and then base model extension so this means the budget will handle all of those different functions which also means that budget can now simply call budget.save like i'm doing it right now and this save function will be coming from the base model okay so this is all fine let's go back to our screen if we can find it there we go and after creating the budget the other thing that we really want to do is we want to make sure that our sheet is closed so let's go ahead and get access to the presentation mode after we have saved we can simply call presentation mode dot rap value dot dismiss and hopefully that will be able for us to save the information to the database all right hopefully now there are multiple ways of checking if the information is saved in the database or not it's always a good idea to go in the database and see if the information is actually saved or not so how can we do that how can we actually see in the database to to debug core data all right so let me show you how we can do that so basically what we're trying to do is that if i go ahead and run this application right now and add a new thing so let's say that i'm adding a budget for watching movies and let's say the budget is 100 okay so it's saved but i don't know if it's saved or not i mean at this point i have no idea if it's saved so it would be good idea to go to the actual database and check that if it's saved or not and one of those things you can do is you can go to product you can go to your schemes and in the run schemes in the argument section you can pass in a debug flag for debugging your core data so i can go ahead and simply say com dot apple dot core data dot sql debug and passing in the flag of 1 which means it's on now if i run the app you should see a little bit more different stuff printed in our case over here and one of the things that you will see is you can see all the different queries that are being run so that's a good thing but apart from that you will also see the location of the file that it has created so let's go ahead and copy that location and now i should be able to open up a terminal jump onto that location okay and now i can see the file the actual file the sqlite file that contains information so i can go ahead and open this up using sqlite3 which is already installed on your machine so i can say app model.sqlite and now i'm connected to the database i can see the schema of the database meaning different tables and i can go through the table called the budget the casing doesn't really matter but you can see that the movie that we entered is there and that is a you know 100 proof that the movie that the data that we tried to enter into our sqlite database it is working perfectly fine so if it's working fine what will be our next step well the next step would be if i run the app right now i should be able to display the data right over here so let's go ahead and see how we can do that now you might be very tempted to simply go to the content view and start typing the fetch request property wrapper which was introduced new uh when we were when the surf ui was introduced a couple of years ago it's not really a good idea to do that and i'll tell you why it's not a good idea it's not a good idea because whenever you use a fetch request property wrapper you have to provide the actual core data model over here and it's always going to return you the core data model so in this case i can go ahead and get access to budget so now what we have done is that we have taken our view and we are now tightly coupled with the core data model the code data model should not appear on the user interface or the ui or the view and the view should not know anything about the core data models because a model can be really complicated but the view doesn't really need to know and that also means that whenever you change the model you will end up changing the view so how can we make sure that we don't expose our core data model onto the view well the first thing we will do is we will create a view model associated with what we want to display on the view and the view model that we are creating is called the budget list view model the budget listview model is responsible for displaying everything related to the budget but we also want to target each item that we want to display so what do we want to display we want to display probably the title of the budget so i'm going to create a nesting or a child model which will be called budget view model it's going to work on the budget so you have to pass in the budget instance and just to hide the budget instance i'm going to make sure that you will you can access this budget outside of it because that's our actual code data model so the only thing you will be able to access would be the view model itself the one that we are just writing right now we will go ahead and create an id property and it's managed object id we will return the budget dot object id now it's up to you whatever you want to expose to the view and the only thing you want to expose to the view so budget dot title and if the title is null then return nothing same thing goes for the total we can simply go ahead and say budget.total and you can return the total okay so this is fine this is going to display every single item of the budget or at least it will give the data to the view so that the view can display each item of the budget but the budget list view model will be responsible for fetching all the different kind of budgets so inside the budget list view model the first thing i'm going to do is i'm going to create a property called budgets which will be an array of budget view model so there we go and what we also want to do is whenever you add a new budget item using the add new budget then it will notify the budget view model that something has been added so if i go ahead and add a new budget item then i don't have to call get give me all the budgets again because it should just already know and that part you can do with creating something called the fetch results controller so let me go ahead and create a fetch results controller and fetch results controller is nothing new it has been part of core data for a quite a long time so we need to make sure that we are initializing the fetch resource controller so fast resource controller equals to fetch results controller and now you need to pass in couple of different things you need to pass in the fetch request all right so now the question is well how do we create this fetch request so this fetch request is going to indicate that what are you trying to fetch one of the good ways of creating this fetch request is in our extension the extension that we created for the budget so i can simply go ahead and say over here all ns fetch request for the budget we will go ahead and create a request which can be a budget dot fetch request we need to sort or we need to set up the sort descriptors also uh because it is a requirement for the nsfetch results controller and now we can return it so let's go back over here now the good thing is that i can simply go ahead and say budget.all and this is going to give me you know that particular fetch request that we're talking about for the contacts we will simply pass in the view context section keynamepad is nothing and the cache name is also nothing so this will allow us at least to initialize our budget list view model with the fetch results controller now currently it's not really doing much just by initializing the fetch results controller doesn't really will not do anything we need to make sure that we put the delegate also and the delegate basically means that whenever the fetch results controller returns you something we need to get access to those things all right so we need to become a delegate for the fetch resource controller delegate so i'm going to go ahead and create an extension you don't really need to create an extension but usually it's a good idea to separate your code in this way i'm going to create or use the fetch results controller delegate and in order to do that i believe we also need to make sure that we are conforming to ns object there we go and inside the fetch results controller delegate we need to implement just one function for now which is called controller change content so i'm just going to go ahead and say controller did change content which means that whenever the content is added deleted removed and all that stuff so this particular function is going to get fired and when this function is going to get fired we will go ahead and get access to the budgets controller dot fetch objects we will make sure that those objects are of type budget array else we're not going to do much and finally we can say budgets equals to budgets dot map and budget view model dot init all right we still need to call super dot init so the main object is initialized that's fine now over here inside the controller did change content which is part of the nsfetch results controller we are setting up the budgets now these budgets are the budget view model array so we need to make sure that these things are always set on the main thread so i'm just going to go ahead and use main actor over here so that we know that it is always set on the main thread the other thing that we want to do is when the budget listview model is initialized we need to just perform the fetch so let's go ahead and do that fetch results controller dot perform fetch again we are going to get back our items we will have to map them just like before and if we catch any problem we can simply display the error all right okay so that's it that is how we will create our budget list controller now if i go back to the content view and if i try to run this let's see if it actually displays anything or not well first of all we're not really displaying anything anyways so let's go ahead and make sure that we are displaying something so we'll go to the content view instead of displaying the text we will try to display something else so we'll display list there we go and we're going to go with the budget so budget list view model dot or just for fun i'm just going to use list and if for each budget let's be model dot budgets this is the array of budget view model we will get access to the budget and now we can go ahead and display the text budget dot title i think let's go ahead and run this and see if the budget is being displayed so you can see that the budget is being displayed what about if i add a new budget so i'm going to go ahead and add a new budget and let's say this is dinner and 100. hmm that's weird because it never really got the notification did it even go to the database let's go ahead and run it again to see if it went to the database or not so it did went to the database but when when we use add new budget to add a new budget it didn't really show us over there so what exactly is going on well the problem is that we never marked our budget list view model with being an observed object now you might be tempted to use state object over here but state object is read only so you can't really assign it over here so we need to use observed object and now i can go ahead and run this and try to add a different budget so i'm going to go ahead and add a budget so let's go ahead and say lunch budget and there we go you can see the lunch budget is we don't really have to refresh anything we don't have to fetch anything since we are using the ns fetch results controller it does all of those things on our behalf now one other thing that we would like to do is ability to delete an item and the good thing is that since we're using the for each we can simply go ahead and also delete the budget so let's go ahead and create this function called delete budget private function delete budget it does have to match a particular signature there we go now we can go ahead and say offsets dot for each we will get the index once we get the index we will get the budget which is a budget view model we will get the index and on that budget list view model we will say delete budget which doesn't really exist and we will simply pass in the id of the budget so budget dot id so we don't really have anything called delete budget so we need to make sure that we have that function so what does delete budget looks like so let's go over here and create a delete budget delete budget which takes in a budget id ns manage object id the first thing we will do is i'm going to go ahead and fetch the budget so context dot existing budget and i will pass in the budget id now for this you can even create a separate function in an extension on the budget model and that would be much better approach budget else return so once we get the budget object we can simply say budget.delete i think it's much nicer and it reads much nicer if you say budget.delete right and we have heart we have taken care of all of those stuff all of those things in our base model okay so let's go back to our content view and hopefully when it deletes the nsfetch results controller which is right here it's going to know it's deleted and the controller did change is going to get fired and we will just get all the stuff that we want so dinner so we'll remove the dinner we'll remove the lunch and you can see it's deleted and if i go ahead and run this you can see that i only have one so it is deleted from the database i can go ahead and add new one over here let's say if i am adding a dinner so there we go so we have created a core data application in the ui when we're using the mvvm design pattern and we ended up using the ns fetch results controller which gives us all the functionality of read loading and all the different delegate callbacks that allows us to refresh the interface without having to uh call the functions ourselves all right so it's a bit of a work as you can see but it does allow us to create a much better flow of the application rather than going to the content view or any view and using fetch requests i mean fetch requests if you are working on a small project that should be okay but in a larger application or a medium even medium-sized application it's not really a good idea to expose your view with the objects that are of the core data objects or the core data to manage objects that's that's very not a good idea because now you're exposing your whole data model to the view or anytime the data model is going to change which it will your view is supposed to change also then so that's not a good idea it's very you know tightly configured system if you do that so this technique using the mvvm design pattern and creating these view models that uses the ns fetch results controller behind the scene that is a much better approach at the end thank you i hope you really like this video tutorial and if you do the best way to support my videos is to check out the udemy courses that i have you can see that i have many different udemy courses ranging from mvvm design pattern swift ui combine core data rx swift and much much more so check out the youtube description for all the links and i really hope that you enjoy my new courses thank you
Info
Channel: azamsharp
Views: 1,042
Rating: undefined out of 5
Keywords: swiftui, ios development, swiftui core data, swiftui core data mvvm, swiftui tutorial
Id: gGM_Qn3CUfQ
Channel Id: undefined
Length: 42min 33sec (2553 seconds)
Published: Mon Nov 08 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.