How to combine Core Data and SwiftUI– Bookworm SwiftUI Tutorial 3/10

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] swift ui and core data were introduced almost exactly a decade apart swifty y and ios 13 and core data in iphone os 3. it's so old it wasn't even called ios back then because the ipad hadn't been announced yet but despite this large gap in time apple's put a lot of work in but these two powerhouse technologies work together beautifully almost the point where they feel like they were always designed to work that way first the basics coordinator is what we call a object graph and persistence framework which is a fancy way of saying we can define objects give properties to those objects and read and write them from permanent storage on the surface this will sound a lot like codable and user defaults but it's much much more advanced than that there is effectively no limit to how much data you can store core data plus you get things like sorting filtering and more plus when you need to really lean on it you get more advanced features like undo and redo support for example it's all there waiting for us along with data validation and a lot more anyway in this project we'll only be using a tiny slice of core data just a little sampler we'll expand on it soon enough which i'll give you a little taste of it nice and early now when you made your xcode project i actually asked you not to check the box use core data that's intentional because yeah that box gets a few boring setup things out of the way but also brings in a whole bunch of example code that you just have to delete it's just annoying and pointless so please don't check that box so instead you're going to learn how to set up core data by hand from scratch in a blank project like this one it takes three steps starting with us defining the data we want to use in our app now previously we defined data like this we said a struct called student for example which say has an id uuid and a name string however coordinator does not work this way because it needs to know ahead of time all the data we want to have in our program plus what it contains and also how it relates to other pieces of data for example a school might have many classrooms and a classroom might have many students they all link together now all this is contained inside a new file called a data model with a new file extension called xc data model d let's make one now press command n make a new file don't choose a file or shift ui view instead look for data and choose data model press next and call this thing bookworm dot exe data model d then press create it'll be created and i'll open up this little editor here which is a data model editor just for this purpose here we define our types they're called entities then we create properties on those types called attributes and it's called as job to figure out at run time the actual database layout so you can use these entities and properties at runtime now for trial purposes press add entity now make a new entity then double click it up here and rename it to be student next click the plus button underneath attributes here we're going to add two one is called id which will be a uuid and another one called name which will be a string there we go and that tells core data everything we have to know to create students and save them so we can now proceed to the second step of setting up core data writing a little bit of swift code to load that model and prepare it for us to use now we're going to write this in a few small pieces so i can explain what happens as we go first press command n make another new file choose swift file this time and name this thing datacontroller.swift then press create now go ahead and add an import for core data above the import foundation i should say that can go anywhere you can put it here but come on alphabet laura is sort of staring i just want to take that um inside here we're going to make a new class called data controller and this thing will conform to the observable object protocol so that we can use at state object with it want this thing to be created when our app launches and stay alive for as long as our app runs now inside here we're going to add a single property while the type ns persistent container this is a core data type responsible for loading a model and then giving us access to the data inside from a modern perspective this is going to sound quite strange but the ns in ns persistent container comes from next step which is an operating system made by a company steve jobs produced in the late 90s was acquired by apple in 97 or so and merchant to apple and became the foundation quite literally the foundation um for things apple made in the future according to has some really old foundations anyway we'll add one of these now we'll say uh let container be an ns persistent container with the name being bookworm and that refers to our xc data model d file right we're telling core data to use our bookworm data model for its container it doesn't actually load it we'll do that in a minute but it does prepare coordinator to load it we're ready to do so now now data models don't contain the actual data they just contain the data models the the definitions of properties and attributes and entities and relationships we made a few minutes ago right to actually load things we've got to go ahead and say please try and load your data um and it will read the save data from disk for a sort of private storage area called documents directory for our app it'll load that back out the real live data for us to work with and that's entirely possible that might fail right that might maybe the data is corrupt for example um but if it does go wrong at this point in your swift career there's not a lot you can do you can show an alert and say sorry the data's bad try and restart the app um it doesn't happen often if indeed ever but at this point it's not what you can do anyway we're gonna write a small initializer in here in it and all we'll do is go ahead and call load persistent stores when our controller is created so we'll say container dot load persistent stores and then it'll pass us a description here and error in and if any error happens if let error equal error all we'll do is say uh core data fail to load and print the error out error localized description like that and that is our entire data controller it's really really simple so the final little step here is to uh make an instance of data controller and then send it into swift ui's environment to say use this thing please now you've met at environment previously we used an earlier project um to dismiss our view there's a dismiss action in there right but it also stores other useful things like our time zone whether in light mode or dark mode and a lot more and this is relevant to core data because most apps only use one core data store at any given time so rather than every view try and make their own store try and read data independently and say we make it once when our app launches and then store inside the swiftly wire environment we inject it into the environment so everywhere else in our app can use it to do this go into our bookworm app.swift file and then make an instance of our data controller we'll say at state object private var data controller is a data controller like that that makes it a controller and keeps it live the entire time thanks to at state object and now we can put it into the swift ui environment by adding a new modifier to the content view like this dot environment backslash dot managed object context data controller dot container view context now you've already met data models that's this thing over here the definitions of the entities and attributes we want to use in our code and you've met ns persistent container which is the actual data being loaded and saved to the device that's what it's doing here well you just met the third piece of the core data puzzle a managed object context these are effectively the live versions of your data when you load data and you change data somehow those only exist in memory until you specifically say i'm done now write it back to the persistent store to the iphone's ssd whatever you're using right so the job of the view context is to let us work with data in memory which is of course much faster than working within on disk all the time always reading and writing objects so we'll load it once work with the memory modify as much you want to and then write out only when you're ready of course when you're ready you still have to write it out you know you can decide not to you could say actually you know forget those changes discard them don't write them but if you do want to write them you say save so at this point we've made our model here and we prepared it for using 50y by loading it and then injecting into the environment here there are still two pieces of the puzzle left reading data and writing it too now reading data with core data is done with what's called effect request we describe what we want how much we sorted or filtered or whatever and then core data sends us back all the matching data for us to use now we've got to make sure this fetch request stays up to date over time so as student objects are created or removed our ui stays synchronized now swifty ui has a solution for this and surprise surprise it's another property wrapper this time it's called at fetch request and takes at least one parameter describing how we want sorting to happen it has quite a specific format so let's start by going to content view adding a new property now we'll say there's an at fetch request sort descriptors empty array var students fetched results student like that so if we break this down we're saying make a new fetch request with no sorting just give it to me in a random whatever you feel like order then put into property called students with the type fetch results of student and from there we can go ahead and use students like a regular swift array but there's one catch as you'll see i'll say in our body there's a v stack the list of our students and one student comes in then we'll do text student.name nil coalescing unknown like that did you spot the catch yes student.name is an optional it might have a value but it might not and this is one area of core data that will greatly annoy you because it has the concept of optional data it knows optionals are but it's entirely different concept from swifty wise optionals they're two different things under the same name optional they mean different things if we set a core data this thing can't be optional like name can't be optional would you what you can do you can say name cannot be optional must be required it will still generate optional swift properties student.name will still be a swift optional even though core data has not optional because all core data cares about is that the properties have values when they are saved so they're optional in swift for the entire lifetime no matter what you do when you press save they must have a value in order to work because saving will check oh this is not optional has it got value no refuse to save and so it has optionals but they mean different things which is really annoying gotta do nil coalescing everywhere as you can see we're going to do a better workaround for this in the future but it's enough for now you can run the code if you want to there is no point because there's no data we're going to fix that by adding a new button to our v stack that will create a new random student whenever it's pressed but to do that we first have to add a property to access the managed object context from our swift ui environment now i'm gonna back up a little bit here this stuff matters we made our student entity up here what happens behind the scenes is inside xcode is core data will make a class for us it'll synthesize one automatically which inherits from a parent class called ns managed object now you can't see this in your code by default it's made for us automatically just like coreml made a model from its machine learning model um which is great but they're called managed objects because core data is responsible for looking after them it loads them from persistent store and writes changes back to us and back to piston store as well for us there's a whole thing for us all these managed objects live inside a managed object context one of which we made earlier when we had our data controller we put into the environment here and as soon as we placed it into the environment it was automatically used by swift ui in our content view here we have a fetch request we didn't say load that from bookworm.exe data model e it doesn't care it'll look in the environment for the current managed object context and use that for its loading and saving anyway when it comes to us adding and saving students we've got to access the managed object context this is another way we'll use the at environment property wrapper we can say give me your context please so we'll say uh at environment backslash dot managed object context there we go var moc because lots of type um oh that in place the next step is to add a button below our list that generates random students and saves them in the manage object context we'll assign a random first name random last names you can see them more easily we'll say there's a button called add and inside there we'll start with two arrays one for first names one last names so first name is an array of let's do genie and harry and hermione and luna and ron and then let last names be granger love good potter and weasley and now we'll choose a random name from both that chosen first name is first names oops name capital n dot random element and then let chosen last name the last names dot random element now inevitably some folks will see this force unwrap here and complain how dare i have a force on the lap and rap we've literally just made the arrays right here and they are constant it's perfectly safe it will always succeed if you hate that fine nil coalesced anonymous anonymous it's down to you anyway now the interesting part we're going to make a student object here using the core data class that was made for us it was synthesized for us this has to be attached to a managed object context so the object knows where it should be stored we can assign values to it just like we do with a struct so we'll say let student student be a new student with the context of our managed object context mock and our student.id is a new uid and student.name now i'm going to do string interpolation chosen first name and then chosen last name like that finally we're going to ask our managed object context to save itself which means it will write out its changes to persistent storage this is a throwing function because in theory it might fail in practice nothing about our current code has any chance of failing so it's not gonna be a problem but we had said oh yeah name is not optional and we hadn't provided a name then saving would fail anyway here it's gonna work so we'll just say try question mark mock.save ignore any errors because there'll be no errors at last we can press command r to build and run our code and try it out in the simulator all being well hopefully we should see a list of information let's find out there we go some students i added earlier and you press add and see your own ones jump in and appear in random places because there's no order to them they appear what they feel like realistically let us slide on in each time and as you saw when you quit and relaunch the app they'll be there for the previous run that it's saved to permanent storage it's not going to go anywhere because caudate is looking after them for us so you might think this is a a lot of learning for not a lot of result right but you now know what persistent containers are and persistent stores you now know what data models are you now know what view contexts are and much much more and you've seen how to save stuff and how to create fetch requests there's a lot and we'll look according to more in uh this project but in later project two in the future but for now you've you've come a long way that was the last part of the overview for this project and so i partly want you to roll back to an earlier version of the code so content view go ahead and just command zed a billion times undo it all it's all going to go away no this is wanted oops crazy that can all go there we go boom okay that's gone but uh make sure you go into the data model and remove the student as well so select the entity and press delete or backspace bank on please keep the model filed though and keep all our data controller that is still good so leave the controller alone make the model file exist but empty reset content view and leave the bookworm app alone too that injects the uh context into our 50w environment we're using these things in the real project you
Info
Channel: Paul Hudson
Views: 1,055
Rating: undefined out of 5
Keywords: xcode, swift, swiftui, ui, ios, programming, tutorial, example, project, code
Id: bvm3ZLmwOdU
Channel Id: undefined
Length: 21min 10sec (1270 seconds)
Published: Tue Nov 23 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.