Saving Data In Your iOS App using Core Data | Zeph Cohen | Skillshare

Saving Data In Your iOS App using Core Data

Zeph Cohen, iOS Engineer

Saving Data In Your iOS App using Core Data

Zeph Cohen, iOS Engineer

Play Speed
  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x
20 Lessons (4h 31m)
    • 1. 0 intro video

      1:01
    • 2. 1 getting setup

      2:00
    • 3. 2 core data api overview

      8:20
    • 4. 3 project explanation creating our model

      10:38
    • 5. 4 locating our managed object model

      7:39
    • 6. 5 creating the persistence coordinator

      9:09
    • 7. 6 setting up our unit test target

      26:42
    • 8. 7 constructing the core data stack

      10:06
    • 9. 8 saving fetching from a context

      31:26
    • 10. 9 deleting from a context

      19:36
    • 11. 10 configuring the persistence coordinator

      10:33
    • 12. 11 creating and saving folders

      17:15
    • 13. 12 intro to the nsfetched results controller

      13:25
    • 14. 13 displaying folders

      16:26
    • 15. 14 deleting folders

      4:51
    • 16. 15 building the notes user interface

      12:38
    • 17. 16 displaying notes

      20:22
    • 18. 17 creating new notes

      13:37
    • 19. 18 updating and deleting notes

      19:25
    • 20. 19 preloading cached data

      15:33
  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels
  • Beg/Int level
  • Int/Adv level

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

49

Students

--

Projects

About This Class

3c84e8d2

All apps manage the display, retrieval, and persistence of data. Being able to save data locally is very important in order to continue to provide a good user experience for the users of your app who are operating in an offline mode or have an unstable internet/cellular connection. Core Data is a powerful technology that Apple provides that allows data to be persistent in memory as well as saved into a SQLite database stored on the device.

This class covers specialized topics around Core Data as well as fundamentals of Test Driven Development. This course is broken down as follows: 

  • Learning what Core Data is and implementing out our Core Data APIs
  • Testing our Core Data APIs using concepts of Test Driven Development
  • Connecting Our Core Data APIs to our app UI. 
  • Learning techniques for preloading data at app startup.

This class is intended for intermediate iOS developers who already are familiar with the fundamental skills and want to extend those skills to locally persist data using Core Data.

Specific Topics Covered In This Course: 

  • Fundamentals of the Core Data API
  • Different ways of persisting data using Core Data (in memory, SQLite)
  • Learning how to utilize the NSPersistentContainer
  • Learning how to create a managed object model (NSManagedObjectModel)
  • How to structure data relationships in the NSManagedObjectModel
  • Loading cached data into a table view using a NSFetchedResultsController
  • Fetching, saving, deleting, and updating NSManagedObjects using a NSManagedObjectContext
  • Techniques for unit testing by applying concepts of test driven development (TDD)
  • Dependency Injection
  • Techniques for pre loading locally bundled data into your application at startup

Technologies Used In This Course:

  • Swift 4 and higher
  • Xcode 10 and higher
  • macOS operating system

Meet Your Teacher

Teacher Profile Image

Zeph Cohen

iOS Engineer

Teacher

Hello,

My name is Zeph and I am a mobile app developer here to teach you how you can make your own mobile apps! 

I have years of industry experience working in desktop and mobile software development and I want to take the skills and knowledge I've gained and share that with you. 

Be sure to check out my YouTube channel Code Pro where I upload videos cover certain programming topics / tutorials. My full length courses I will be uploading here which will go into much more depth and detail. 

Feel free to reach out to me with any questions you have as you take my courses.

See full profile

Class Ratings

Expectations Met?
  • Exceeded!
    0%
  • Yes
    0%
  • Somewhat
    0%
  • Not really
    0%
Reviews Archive

In October 2018, we updated our review system to improve the way we collect feedback. Below are the reviews written before that update.

Your creative journey starts here.

  • Unlimited access to every class
  • Supportive online creative community
  • Learn offline with Skillshare’s app

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. 0 intro video: Hi, I'm your instructors F And in this course you are going to learn how to save data in your IOS app using core data. Most APS require data to be stored offline to keep the user experience smooth and uninterrupted. Core data is a powerful A P I for managing offline data and this course we're going to learn the basics of core data and not to use it. How to integrate core data into your IOS app. How to fortify your core data code through unit tests and elements of test driven development and finally had to pre load data into your app when the application first starts. This course assumes you already have some familiarity with IOS programming. This course is broken down into individual many lectures and by the end of this course, you will have built an app that looks like this in this course will be using the X Code, integrated development environment and the Mac OS operating system. By the end of this course, you will have a good understanding on how to persist your application data for offline usage using core data, let's jump in and get started 2. 1 getting setup: the first thing we want to do is get our project set up to use core data. So in X code, go under file new and project. And when you select that, you'll be prompted with the template like this. What we can do here is start off with a single view application and we want to make sure that use core data is checked basically excludes. Gonna insert all of the data models, the coordinate a stack and a lot of things that we need basically the boilerplate code toe work without of the box to make sure that's checked off. So go ahead in name your application and then we'll talk through all the various components that are inside of the project. Now that you're inside of your project, let's take a look at the components in here. So when court date is automatically added to a project inside of your app, delegate, you're gonna see a court date of stack basically boilerplate code that was initialized, created by ex code. That's the starting foundation from which you can build off of for using core data and you're going to see things like a persistent container. You know, methods for saving a context and it made seemed a little confusing at first. But we're gonna pick apart what these things are, how they work and try to understand them. And another thing you're going to see here in the project structure is in your file hierarchy. Down here, you're going to see a file with an X C data model extension, and typically it's gonna be named after the target of your application. In my example, it's named core data tutorial. And this is gonna be the visual object graph for the relationships of entities that we're going to create in court data and how you can visualize relationships, see the properties, the data types of properties, so on and so forth. What we're going to do in the next two tutorial is start to go through the core data stack piece by piece, and we're gonna take an iterative approach building off of each component that we learn. And so we fully understand, had a utilized core data to load and persist data in our application 3. 2 core data api overview: before we begin writing any code, I want to take a look at the documentation for core data. And you can find this on the Apple Developer site under the what is coordinated section. And it gives a pretty good overview of the core data framework, the features and how it works. And a lot of people incorrectly assume that core data is a database. That's all it is, is you save your data to database. He read your data from a database, but that's incorrect. Core data is a framework that used to manage model layer objects in your application. It's basically an object graph. The framework manages three D objects that come in the changes that are made. And yes, you can save that graph to a database in the form of a sequel light file. But there's also a few different other formats for persistence. And if we go under here under the advanced topics and look at the persistent store types and behaviours, you'll see that there are different types. There is what's known as an in memory persistent store. There is disc based, which is what we refer to as the sequel light or commonly what we think of when we say coordinator is a database. There is also binary, and there is XML now. The most commonly used formats are going to be a sequel. Light and in memory and in memory is exactly what it sounds like. You can hold your entire object graph in memory in your application and perform all the same upsets of operations that she would normally do, except it's not saved to a file on disk. Another point to note is that the XML store type is not available in Iowa. This is only a future of the core data, a P I for Mac OS. Now that we have a brief overview of what core data is as a framework as an application programming interface, let's go and take a higher level. Look at basically a visual diagram of how the whole core data stack essentially operates in our application. So here's a visual overview of the persistent store types that core data supports, the one that you're probably most familiar with. Or like I said before, what you think about when you want to score data as a database is the sequel light configuration. When you use this configuration. A sequel light database file is created locally on your actual device inside of the application directory, and that is where your data is safe to, and that's where you load your data from. And this is a very commonly used type in most applications. The next type is in in memory, persistent store type and all of your object graph resides in memory, and it doesn't save it to a physical file. But the benefit of this is when you want to add in unit testing. Sometimes when you're testing your code that utilizes your core data stack, you want to not necessarily save everything to a physical database or have to go through the pain of setting up a another physical database for unit testing. When you can just do everything in memory and test that your logic at that level works fine across the object graph. Now XML type is only available in Mac OS. It's not supported in IOS, so for the purposes of this tutorial, we're not really going to cover it now. The fourth persistent type is the binary type, and this is useful because it's going to occupy the least amount of disk space, and it loads Theo entire object graph into memory very, very quickly. In certain situations Where it may be, the memory footprint is an issue or the size of your actual file matters. Then using the binary persistent store type can be helpful because again, it loads the whole graft incredibly quickly, and it occupies a very minimal amount of space. And using a binary store type really is going to depend on your individual use case. But for this serious, we're going to focus on using the sq light store type and the in memory store type because they're the easiest to understand in the most commonly used. Now, let's take a look at the visual flow in the core data stack. So really, the first place we're going to start at is the managed object model. And like I said in Part one, the actual XY data model file that visual file that you can create your models in to find the properties and the relationships in is going to encapsulate the graph, essentially the object graph. So if I want to create maybe a new record for let's just say like a person from trying to store some data about a person in my application. I'll create that person inside of the model. And what that does is it exposes a person managed object here, and the managed object is nothing more than a object that can be saved within core data. It has properties. It has attributes and additional metadata for compatibility with core data. Now all managed objects are created inside of a managed object context, and you can think of this as a scratch pad or an environment where objects are created in and ultimately, when we want to save our data model, maybe after we've made some changes to it. Those changes from the managed object context ultimately go to what's known as a persistent store coordinator. And now this is where it gets interesting because the store coordinator is almost the final destination for persisting the graph at that point. Now the store coordinator is initialized with the manage object model, so it knows about all of the various models in our graph. At the same time, the persistent store coordinator contains the store types or the different configurations we may have specified, so we may have wired up are persistent store coordinator to use a sq light backing store. So, in an example, where? Let's say, I wanted to save the change to my graph, and I call the Save Function on my context. Well, that's gonna bubble down into the coordinator. And ultimately it's going to write those changes to my sequel Light file on disk and be persisted there. Obviously, we can configure a store coordinator to work within in memory, persistent store type or a binary store type if we want Teoh. And like I said, XML is not available on IOS. It's just on Mac OS now. This might seem confusing, and it kind of is if you're brand new to this stuff. But a good data flow diagram can kind of break down what's going on, and it doesn't really get that much more complicated than this. So let's go back into the code and pick apart the coordinate a stack that Apple provided us for default by specifying that we want to create a project that uses Gore data for AP. Delegate contains the full court data stack, which is mainly the N s persistent container. Although I didn't visually diagram this. We've already talked about all of the components that the persistent container has. So let's commands click on and his persistent container and read up on the class documentation. This class was introduced in IOS 10 mainly because the core data AP eyes before this were difficult to work with the head of steep learning curve. Just like the diagram I showed you, you had to configure persistent store coordinator object context loading models in dealing with all of that stuff in its cumbersome. The persistent container is a rapper AP I around all of that, but it makes things a little bit easier to work with. The persistent container provides the context for which we can use. We have a context that we can use for the main thread or you elements. We have a context for loading things in the background or performing background tasks inside of a context. We can initialize a container with the manage objects model, and we can see the persistent store coordinator and the persistent store descriptions the types that correspond with our container. So everything we need is inside of this class for us to use, and we're going to take advantage of using the persistent container because of its simplicity. Obviously, you could still do everything the old way, but there's no reason to and we gain all the benefit from using the persistent container. 4. 3 project explanation creating our model: it's finally time to start coding, which I'm sure you've been patiently waiting for. Now, let me reiterate here that this is in intermediate to somewhat advanced course for an IOS developer. And if you haven't built an application yet or have learned the fundamentals of violas development, I definitely recommend that you take a look at my Iowa's fundamentals course for beginners before attempting this course. Because this course assumes that you already have the basic understanding and knowledge of IOS, app, architecture, relationships, you like it elements and how well everything is connected and in this course builds on top of that. With all that said, let's go ahead and get started. If you remember from video to the overview of the diagram that we have, the first place we need to start is really with our model are manage object model that's gonna contain the relationships of the data, the types of data that we want to support in our object graph. So that's gonna be this section right here in my little pdf diagram here. And if we take a look because this is a good Segway into the project of what we're gonna build, we are going to emulate the notes up in IOS, and that's one of the pre installed applications on your phone that you might use to quickly jot something down. And it supports AH, basic data structure that we're going to follow, which is the concept of folders and folders can contain notes, and the data model for our project is going to be relatively simple. It's going to have folders that can contain one too many notes, and we'll be able to create multiple folders. This keeps things simple enough to understand. Thecornerscores AP I at a more fundamental level without getting into a super complex data scheme. The supportive behaviours that our project is going to support for the folders and notes is going to be the ability to create and to save new folders to create new notes and save those notes to folders to be able to edit and save an existing note inside of a folder to delete notes from folders to delete folders and all of the notes that air inside of that folder. And finally we're going to learn how to import notes and folders from a local file that's bundled in the application that runs a synchronously in the background. The data models for our managed objects are going to be simple, and they're going to be a folder and a note. If older contains a title for the folder and he created Date and the Note contains a no title full note text and also have created date for the note. Now that we have a high level overview of our data model and what it's going to consist of , we are ready to go into our managed object model in core data and begin to build out our folder and note entities back in the root directory of the project. Here. Let's go to that XY data model file. Typically, it's named after your applications Target name and inside of this file, uh, it's completely empty, and we're going to create two entities the folder entity and the Note entity, as are managed object models. And to do that, it's pretty simple. Down here in the editor, there's a little plus icon, and you can hit add entity, and that creates an entity for you here. If you double click on it, you can rename it. So let's start with the folder. We'll call this folder and let's go in to find some attributes. Attributes are the properties that are contained on the model, so if you have the plus icon, he will be given an attribute and let's call this title. And then you can select the type from the drop down here. Now the type could be various data types, such as integers, decimals, doubles and floats, a string, a bull date, binary types. You, you, I ds. Your eyes transform a bles. So it pretty much any data type you can represent here. Sometimes the question is, should you store something in core data if it's very, very large, as opposed to storing and the file system? And that's a discussion outside of the scope of this course, but it's just something to keep in mind. You don't want to store everything in coordinated, depending on how large it is and how often you have to access it. But for the title, it's simple. It's gonna be a string for the for the data type, and we'll create one more attribute for the created date. And so we'll have to bless Icon again, and we'll just call this create a date and pick the type, and we'll pick a date type for that. And so the folder entity is good to go. Now let's go ahead and create the note. So plus, for entity again, Double click to rename will call this note and pick the attributes. Never this one. I'll give it a title and will be is a string again. We'll do another attribute for what we consider the full text or the full No text. So I was gonna call it full text. All right, full note and we'll go ahead and pick the string type here and one more for the created date. And you might be asking, you know, why would we be storing dates? What use do they provide? And you can see Consort notes by date. See one have created. So it's useful to keep track of this information for certain models that, you know, have a time stamp for when they were created and how they could be represented. So now that we have these attributes to find, let's go ahead and come down here to the bottom right, and you'll see that there's this editor style and currently we are on the table, but let's switch it over to the graph now. The graph is interesting because it gives you the visual representation of your models. It could get very complex as you start to add more and more entities in more relationships . But you can see here that there's no relationship connecting a folder to a note there simply to separate entities, and they're not connected. So how do we create the connection between these right? We want to have a folder that contains many notes. How do we do it? And so that's what we're gonna do next. So let's go back to the table view here by clicking this and let's start with the folder and there's a relationships subsection over here and again. We're gonna go ahead and create the plus or click the plus icon here, and that creates a new relationship. But what does this title relationship really mean? It doesn't mean much in this context. So what we want to say is this relationship will be called notes, meaning one folder has many notes. The destination needs to be selected as a note, and that's saying that folder has a destination to notes. And that relationship is titled as notes and an inverse relationship we need to select in a moment. So what we need to do here is if you go to the model inspector, you see if I've got this selected here. Yeah, there we go. If you go here to the third icon for the model inspector and you go down to the type, you'll notice here that it's a 21 So if we go back and switch it to the graph view here, you can see that I've just created a relationship from Notes. Rob. Sorry from Folder two notes. And if we go back into the table of you, we'll select too many in the drop down for the relationship notes. And let's go back into the graph and see what changed. Now you can see that we have a double arrow here from notes from Folder two notes. Let's go back one more time into the table and select the relationship. And now that we have that established, we need to go ahead and select the note and to find the relationship of the note back to the folder. So what we can do here is click plus again and you'll see that it gives us the default relationship that we can change that and we can call this folder. The destination will be folder and now with the folder Destination selected. Let's go ahead and take a look at our graph. And so now we have a connection between these two entities. One note has a particular folder that it belongs to that can go back here. Beer be traced back to the folder. One folder has a collection of notes that lead to the note entity here. Now, one more thing we need to do is set. The inverse is X. God's gonna warn us about this, and that's defining the inverse relationships between these two on the relationship itself , starting with Folder now that they're connected, we want to say that for notes, has a relationship back or an inverse back to folder. And then if we go back to the node entity here and select the folder relationship, we want to make sure that the notes in versus selected and these drop downs air only gonna be populated once you've created a relationship between the two entities, and this basically helps core data to determine that the object graph is going to make sense that there's connections on both sides. It's easy for core data to determine that this entity is associated with another entity, and it warns you if you don't put them, it's definitely something you want to keep in mind. Now again, the final structure of our entity diagram is going to look like this. And for this project for this course, that's his advances is going to get. But like I said, in many projects you will see tons and tons of these entities lettered in the file connections going everywhere. And sometimes it can be difficult to sort out how everything works, but at a basic fundamental level. These are the foundational steps for building out your core data model. 5. 4 locating our managed object model: in our last video we generated are managed object models, and if you go back to the managed object model, you'll see we have the folder entity and the note entity here. But you might be looking in your file directory and not sure where to find these managed objects. And as it turns out, if you go into for example, in APP Delegate and let's start Typing folder, you'll see that X Kurt is automatically beginning to pick up a folder model from somewhere . And if we commands, click on this one in particular. You'll see that we do have a class boulder plus core data class, and it was automatically generated by Ex Code in the same thing for the note. If you go back and start typing and note, you'll see that we get a symbol for it. And if we commands, click jumped a definition. There's also a note class as well, automatically generated by ex code. Now the question is, why are these models generated automatically? And if you go back to the model, he file itself. And if we select one of the entities and go over to the attribute inspector, there is a little drop down here called cogent, and if you expand it, there's a few different options. There's class definition, which is selected by default. There's manual slash none. And then there's a category slash extension. In older versions of X Code, these files were usually created manually, meaning that they would be created in your project structure here. But in newer versions, this default setting of class definition is applied by default. Now I'm going to show you how to do it the manual way. However, I'd suggest you don't do it in your project because this can mess things up if you don't do it correctly. There are some X code quirks with how this is done, So if you want to know how to do it, you can watch this section. But I suggest you follow the exact steps I did you source control and revert your changes if something breaks. But basically what we do is this. We select the entity that we want to generate a manual file for, and we go here under the Attribute inspector. Now you'll notice class definition is selected. What you do is you change its manual slash non first and I'll do the same thing for the note model here. I'm gonna go where it says class definition and switch it to manual slash none. It's also good to verify that the settings persisted. Sometimes X cud doesn't always remember the setting that you made, so it looks like those air good there. Now, once we made those changes, we want to go and make sure that we have our actual manage object model selected and go into the editor option here. Now, if you pick a different file like let's say, storyboard, for example, and then go to the editor, you'll see that the options are different because its context sensitive based on what file you have selected in your project hierarchy. So go back, select your model, go to the editor and go down to create managed object subclass. It's gonna ask you which entities would you like to manage under your model? And the only model that we have is through the core dated notes app model. So that's a little select. And then it's gonna ask you which entities you want, and I'll select both folder and note and hit next. Um, what we can do here is reorganized These I understand your group Call it models for data models and we can just drag these four files into the folder. And so here's what. We're gonna have a problem. Go ahead and build your project. You're going to see it that it will not compile. And this is why I didn't want you to attempt this, because knowing how toe debug this could be tricky. But basically, you have duplicate files in your derived data folder because the previous class definitions were there, the auto generated ones and then we created manual ones on top of those, and then they're still there. So what we need to do is clear derived data to to fix this. If you go to X code and then go to preferences, go to locations, there's going to be a derived data folder here under um, yeah, there's a default location, and you can select that little gray icon here and you're going to see your derived data folder. Um, right in here, what you want to do is just right. Click move to trash. And before you do that, if you're curious, you can look inside of it and see, it's just all the cached Ah, information on your project. Now that you've deleted that, try rebuilding your project. Aaron. Let's try cleaning the build folder and let's see here. Hopefully, this will work. It's a little bit quirky, but yeah, that worked. So that was a two step process, actually, three stuff you had to switch to. Manual slash None. To regenerate those models, clear your derived data and clean your build folder to get past that problem. And it looks like I'm building successfully now, but you can just use the auto generated ones. It's fine, but I can show you some more information with the extensions here that I couldn't show you is easily, um So now if we look at the that's right, So we've already seen that we have the class and Exco generates an extension here on that class with the Properties notes title created date. All of the attributes that we specified in our actual visual manage object model here are represented in the extension. So if you see here looking at our note, we have created date, full note and title, and if I go to notebooks, core data properties, we have parent folder that the note belongs to is represented by a property called Folder Right here. That's basically how you get back up to the parent. Same thing for the folder, plus core Data Properties extension here. If we go and look at our model here, it stores the created date in the title as properties on the model here, and you can see that there is a set for notes and that represents the relationship here notes. Because our folder has one too many notes in it. And now there's also some additional utility methods that get auto generated for us. And that just is something like. Add two notes where we can add a single note to a folder. We can remove a note from a folder. We can add a set of notes to a folder and remove a set of notes from a folder so these air useful utility methods to know about as we start to develop our core data AP eyes. Now let's go back over into the APP delegate and take a look at our core data stack here Now, exclude automatically generates thes. But what we're gonna do is we're gonna delete the container property, persistent container and that safe context method here. We'll also delete it from the application will terminate. And if you scroll to the very top of the file, we'll also delete the import core data in the next video. We're going to create our core data stack from scratch, and we're not gonna keep it in the APP delegate because that's not a good location to store one of the foundational AP eyes that your application will need to support. 6. 5 creating the persistence coordinator: if you remember from the last video we deleted all of the core data stack from the APP delegate. And in this video is where the fun really begins. We're going to start building our persistence coordinator from scratch. So to get started, let's go ahead and go over and create a new folder so we can right click and create a new group. You know, just call this group persistence, and this is gonna contain our entire A P I for our core data services. Now, if you did create a another group of core data models from the previous video, if you did the manual Ennis managed object files like I did, you can just simply drag that folder into your persistence folder and we can organize everything under that. Now, once you're ready to go ahead and right click and will create a new swift file, we'll call it Persistence coordinator. And the very first thing we're gonna need to do in this file is to an import encore data, and that's going to give us access to all of the core data. AP ICE that this class is going to encapsulate directly. Now we go in and in this class persistence coordinator. And if you don't want to choose that this name, that's fine. I'm just to choosing it because it's generally going to describe the behaviour of what we're going to implement in it. Now, let's ah, think about what this is gonna need to support. The persistence container is essentially dependency that we're going to feed into this class. So what we want to do is create a property called the Persistent Container. And if you remember, this was the basically the coordinated staff that's contains the managed object contexts, and it loads the model. And it's a nice all in one way to wrap these AP eyes. So what we'll do is we'll created initialize er in it, and we'll give it a persistent container as a parameter of type and its persistent container inside the initial Isar will go ahead in the sign the parameter to our property and so far, so good. So we won't have any compile errors there with regards to initializing that let property now. Additionally, we want to keep track of the managed object model itself. We would like a way to be able to load any sort of manage object model, Not necessarily one that's bound to our application. If you remember, the manage object model is this file. It contains all of the different entities. So it would be great if we could have this class be agnostic to the type of model that it's fed most of the times. Most of these AP eyes were most of these classes only work with one type of model because it's hard coded. That's something that we don't want. So what we can dio is we can create another property and this will be an N s managed objects model. And what Weaken Dio is inside of our initial Isar here will assign that model to the model of the persistent container. And now, if you for for gotten if we take a peek at the documentation of the persistent container, this is back from one of the earlier videos. The N s persistent container contains the manage object model is a property as well as the persistent store coordinator and the persistent store descriptions which allows us to change the type. So if we wanted an in memory core data stack, we could provide a in memory store description as opposed to a sequel light store description that's going to save our data or persist our data in a sequel light database. So these are important things to be aware of. Um, also there is the manage object context, the view context. There's a way to create a background context for handling things in the background thread. And these are all very important properties that we need to expose to our persistence coordinator to use and we will build off of now. What I want to do here is go into the split editor and I want to keep the documentation open on the right for the container here so that we can reference it. I then want to go back to our persistence coordinator, and I want to take a look here because right now we have an initial Isar that can build from a container. But that's not necessarily enough. We might want to provide the capability to switch from a sequel, Lights persistence coordinator to an in memory store type, and it's difficult to do that by just passing in a container because we would have had to already have built this ahead of time. So we want to have another initialize er that gives us the raw materials needed So we can build a persistence container directly from those parameters with a configurable store description so we can create a in memory coordinator or a sequel like coordinator. So what we can dio is create another initialize er here will be in it with a model and a store description. And what we can do here is copy and s persistent store description. And inside of the initial Isar, we can assign the model directly and self dot persistence her persistent container we're gonna build inside of initialize er so what we'll do here as well do it s persistent container and take a look at the initial Isar. So we have two different kinds we can use weaken build it from a name or a container usually named after the name of the model. Or we can use a name and a model that we passing directly, and we're gonna go ahead and use this one instead and we'll do here is pass in an empty string for the name and I'll explain why in a minute and will pass in the model for the parameter here and that will go ahead and complete the excitement. Now you might be questioning. Why did I pass in an empty string for the N s persistent container parameter when I passed in a model that doesn't make sense and that's a valid point. Now, I pulled up the documentation for this initial Isar so we can see what's going on here and the initialize er for the N s persistent container that takes in the name of the container in the model and they tell us that the name is used by the container and then this is the model to be used by the container. However, there's a little section here that says that by default, the provided name value of the container is used as the name of the persistent store. However, passing in the manage object model object overrides the look above the model by the provided name, and that's the behavior that we want. We want to be able to assign the container to be built from the model. So in this circumstance I don't care about the name. I can pass it an empty string. The documentation tells me. It's gonna be overridden anyways. Now are initialize er's air done, but there's still more work to do. We need to implement the functionality to load our persistence Coordinator provided the values that we gave it. Make sure that everything set up and we need to expose the AP eyes for fetching managed objects, saving managed objects and all of the things associated with that. But rather than go too deep into the weeds and doing that in each video here, we're going to try a different approach. We're going to set up our unit test target, and we are going to implement some unit tests along the way at each step of how we build out our coordinator, so that by the very end we're going to have adequate test coverage for all of our AP eyes and initialize and properties. We're gonna be able to load our entire persistence coordinator using an in memory store, and we're going to understand how this truly can work with any type of manage object model , which is what we want, and it's going to make more sense as we implement each piece along the way. So we're gonna apply some context of TDD or test driven development. And if this is new to you, I think you'll find it very beneficial in helping you understand things that might seem a little bit complex. Well, at the same time making sure that your code works and providing adequate test coverage in the next video, we are going to go ahead and do exactly that. Set up our test target and start loading in a test manage object model into these initialize er's and then start building step by step throughout that approach. 7. 6 setting up our unit test target: In our last video, we started to implement our persistence coordinator and we implemented a couple of initialize or methods. But obviously there's still more work that we needs implements in this class before it's fully usable. Now, since this is going to be one of those foundational AP eyes, that's gonna be the bread and butter of all of our transactions going through core data. We want to make sure this code works. So this is a good time to transition into some concepts of test driven development. Also abbreviated as T d. D now from a pure, test driven development standpoint. Generally, you write your unit tests first, and this is before you've even written any code. Your test will fail right away in the beginning. Obviously, because you haven't written code that can pass your tests, you then go ahead and write the code to pass the standards of your test or what you've defined. And then you iterating that fashion writing a test first, then writing the code, making sure the test pass, rinse and repeat. Now we're not going to stick to a granular approach like that, but we're going to still take the concept of testing what we right, So we may not necessarily ready tests first before writing the code, but we are going to take some elements of test driven development as we start to move forward in this tutorial, Siri's. So if this is new to you, this is a great way to be introduced into test driven development. And this will be a very beneficial as you move forward in developing your APs or future projects when you apply these principles going forward. And let's take a look at our project configuration to support unit tests, we have to have a separate unit testing target. And when I created this project in the beginning, I created the application target and I allowed a check box so that a unit test target would be included. If you look at the root of the project structure, I have my IOS application target folder here and below that, I have a unit test target here. Another way you can tell you have it configured is if you select your project file and then you go down to the target section, you'll have an application target and a unit test target. If one was created. Now, if you don't have this or you missed the step, No worries. It's really simple. All you need to do is go to file new target, and when that comes up, you're gonna look for a unit test target. And if you go down under test, you'll have a you I testing bundle and a unit testing bundle. The unit testing bundle is the one you're gonna want to select and create in your project. Now, since I already have mine created, let's go ahead and take a look at how we can share files in Resource is with the test target. The test target is kind of similar. In some ways, you can load resource files into it, share it so that you can use things like images or something of the core data model in your tests that are required to test some of your other AP eyes in your code. So if I select, for example, my core data model file here and I go over here to this little icon, the final Inspector, with that selected, you can see that there's what's called target membership right now. This file in particular, is only available to the IOS target. So if I wanted to use this model in our unit tests, I need to go ahead and check off the core data notes. APP target. So basically, this file has dual membership. It belongs to the unit test target so we can use it there. And it's also a part of the application Target itself. Or basically it's bundled into the application bundle. Now let's take a look at the unit test file so we have this core data notes AP Test Start Swift file And this is the unit test file that's created by default with our unit test target. And the way it's structured is X C test is the test framework that all of the unit tests basically derived from. So you'll have a class, whatever the class name is, and it's gonna drive from typically XY test case, and it's gonna have some methods in it that look like this. There's gonna be like a set up method, a tear down method and then an actual test. Something method. Whatever you're trying to test. Typically, you'll import the XY test framework at the top of your tests, like here and the at testable import basically allows you to test all of the components that are inside of your module. Basically, it exposes the source code from your APP. Target of your framework. Target to your unit test targets so that you could test them, which is really nice. Now the structure of a unit test is pretty simple. There is a set of method that gets run before each unit test, so this is like a place to set up some initial state. Maybe Leadsom resource is for your test, and then they will be used by the unit test itself. And then, after the test completes were fails, the tear down method is called that basically resets. Any state or any variables of resource is that you want to clear before the next death starts or anything like that. Now, let's go ahead and just go for through a basic example of how to write a test from scratch . So let's just say I was writing a simple adding numbers so it's gonna look like this is gonna be test. Add two numbers and it has to include the word test in the function name. Otherwise it won't register as a unit test. And let's just say we want to have lead expected. Some equals five. Let's what Number one use to let number two equals two. I'm just these air arbitrary examples. And then how do we actually test something or assert something? Well, we do something like this. We can create an XY assertion which can test whether something is true or false. Nil or not. Miller, just about any case that you're trying to validate in your test so I can assert something like XY assert. True Rexy assert number one plus number to equals expected some, obviously two plus two does not equal five. So how do you run a test? There's this little play button here and usually have to rent one so that it builds you test. And now if I want to run this individual unit test, I can run it like this, and it will only run that test and you'll see that it's going to fail because two plus two doesn't equal five. So you can see here that my test failed. Now, if I wanted my test to pass well, okay, two plus two equals four, so I'll change my expected some 24 I'll go ahead and run my test again. And let's see that the test actually passes. And there you go. Green check marks mean your test passed and that's good. Red is bad. Now, this is gonna be the basic structure of how we kind of operate. So obviously, this is the example Unit test class that came when we created our test target. But we're going to do next is create a new unit test file and we're going to go new file and it's gonna be in a test target. We'll do here is select a unit test case class don't select you. I test. That's a different kind of test that we're not gonna get into in this tutorial. Serious. And I'll go ahead, Nick it, and we'll call this persistence coordinator tests and ahead and create it. Make sure it creates in the test target, not in the app target. And so now we've got our basic set up here. One thing we can do is do an at testable import for the application name or the module name . In my case, core data notes, app. And if I go back to the other file. I can just make sure I copied that correctly, which I believe I did. And so we're good here. And I'm just gonna go ahead and the elite, the default unit test class that we came with a target because I'm going to use the persistence coordinator tests that we just created now. So what we can do next is actually clear out some of this test example test performance example. We're not gonna use it. And what we can do, then is go ahead and start setting up our test to test our initialize er's for a persistence coordinator. Let's go ahead and open up the assistant editor so we can get our persistence coordinator on one side and our tests on the other side so we can kind of visually see what we're doing here. Now, let's take a look here. We've got to initialize er's and we will want to read unit us for them. We're going to start off with this initial Isar here. So how do we test it? Uh, when we look at this code, we can see that we are providing a managed object model and a store description and we're signing this property so right away we can see that we can initialize this class and it should have seize properties assigned and we can verify that those properties are assigned . So how do we write the test now? We can start off by creating other function, and we'll start off with putting the name test has the prefix because XXXII test requires you to include tests before the name of the actual function or the unit test itself. But what do we name it from here now, with the unit testing, there are a couple of different ways you can name your unit tests or different practices. Ah, one such practice in the practice, which I'm going to use in this tutorial Serious is gonna look like this. So you're gonna typically have a test underscore the name of the test with some kind of state and some kind of expected result or something that should happen at the end. It's a different kind of naming convention than you would ever see and writing your application code. You're not usually gonna include underscores like this with camel casing in between, but it basically separates what the test is any kind of state or expectations that go into the test in some kind of expected result as the output. That's generally the formula here. Now I'm not gonna necessarily split thes. I tend to combine them, and you don't have to stick to this exact naming convention. There's a few different ways to do it, but I just want to explain in advance some of the reasoning behind why this is gonna be written this way. Let's go ahead and write our test, so I'm gonna create a function here. Funk test initialize er and I generally tend to combine the test with the first part of the name, although you don't have to if you don't want to. I know for certain state, I'm going to say that I am providing the dependency. So test initialize er provided managed object model and store description initialized or coordinator initialized successfully. It's a little long winded, but let's go ahead and actually break down what this is gonna look like. So we're obviously testing this initial Isar and let's go ahead and start writing it out. Let Persistence Coordinator equals persistence coordinator and the model in the store description now you're gonna think. OK, well, where do we get this model from? Obviously, we have to load it from somewhere, and the model is going to be inside of the application bundle or in the unit test bundle. And if you remember, we share the manage object model with our unit test bundle. So we need to do is load this from the bundle and provided as a dependency into the test. The next question is, where should we load this model? From where In our unit test code. And the set up method is really the appropriate place to do this because it's the logic. It's the method that's called before each test. So what we can do here is will create a new property right above the set up method. And we're just gonna call this the managed object model of N s managed object model, and you might have to do an import encore data in your test to target here to get a reference to s managed object model. So import core data. And now we shouldn't have any complaints there. And I'm going to go ahead and implicitly unwrapped this here. Ah, and I'll explain why in a minute. But basically the next thing we're going to need to do is load are manage object model from the set of method. Now, the way this is gonna work is we're gonna load this from a bundle. And so what we need to do is get the bundle that belongs to the unit test target The unit test target is kind of like an APP target. It can contain files it can contain code such as the tests that were writing that are essentially compiled into the target. However, by using an at testable import statement here were able to see all of the source code from our application target that we want to test. And so that's the benefit here. Certain things like bundles exists in the concept of a test target, and so we can load. Resource is almost in exactly the same way as we can Load. Resource is from an application target. And so that code looks like this. So we'll do a let bundle equals bundle. And when we look at what we're gonna use here, we're going to use the class. So what weaken dio is we can give the name of the class, which is in my example, persistence coordinator test. So persistence coordinator tests dot itself gives you the name of the class. So we're getting the bundle that basically corresponds or maps back to wherever this class came from, which this class came from the unit test target. Which is why we can load Resource is from a bundle in this fashion. Now, the next thing we want to do is actually go ahead and load the managed object model. So guard, let your l equals bundle dot u r l for a resource with extension, and we'll go ahead and come back to that. But the extension is the m o m d like that. And let me just go ahead and set up the else block here. So, um, fix my guard statement. If for any reason we fell to load this, which would be unrealistic in this scenario, we can just to the next city fail, which basically fails our unit tests. We can put a message inside of the fail saying that something relevant, like, failed to load the test dependency and be done with that there. Now, if you go ahead and open your project, Uh, navigator here. And so we're looking for where this model lives. If we go ahead and do a core data notes up, so there it is, Coordinated that XY data model. Now, this is going to go buy a dot m o M. D file extension, even though it doesn't appear as such. When you look here for the file extension, you see an xy dot data model. In fact, that's going to correspond to an M o M D extension when we try to load it from a bundle The M o M d name or the extension is Thekla piled version of the XY data model. So we have the visual representation here when X code goes and compiles this file down to the basically stripped down version to use when it's loading everything at runtime, it gets compiled down to this M. O. M D format. So we're basically loading the compiled version of our model, which is why we have to use the mong extension. The name we're gonna want to provide, however, is gonna be the name of the file that my example coordinated notes app in your project and your example, it may be whatever you named it or whatever your name, your project now. So what I'll do here is do core data notes up like that? And then what we'll do ago is go ahead and extract the model from the URL. So we can do here is let we're going to let model equals and s managed object model, and we'll see here that it can take contents of the URL. Now we can provide the URL that we just extracted from our guard. Let check. And better yet, what we can do here is make this a compound check so I can go ahead and Atacama to the end of my bundled out euro and we can go ahead and Shane this in there. So that way both of these checks have to pass. We have to get the earl and we have to get the model. If either one of those fails, we fail the test. So so far, so good there and then weaken dio managed object model equals the model that we just loaded from the file. And that's how we load it. We can go ahead and comments out and just verify that everything builds and that are set up code is working as expected, which it should be. And we'll see a build succeeded in our tests will pass because we don't have anything running in it right now or anything actually being realistically tested, let's go back down into our actual test here, and what we can do next is create this persistent store description. That is the other dependency that we need to feed in. And basically it looks like this so we can create a description as follows and as persistent store description and description dot type is the type where we're going to say , Hey, this is an in memory type or this is a sequel light backed type, so on and so forth And what we can do here is we can go ahead and test with the in memory store type for the sake of the unit tests. So N s in memory store type, and that sets the type. Now if we commands, click on this and let me see if we can take a deeper look here again. Based on what I said in some of the previous videos, we have the different types represented here inside of the persistent chorused ordinator AP Ice. There's thesis, equal light store type, binary and memory. So those are the representative types available for Iowa's. Now that we have that done, what we can do here is go ahead and feed those dependencies in now because we have assigned are managed object model up here, that's fine. We can just go ahead and to manage object model and for the description we just pass in the description. And so far, so good. Now let's run our test and verify that everything bills and runs in great our test passes Now, obviously, if you're new to unit testing, this doesn't really make sense. We didn't really test anything, right? What do we need to do next? We need to write some assertions. We need to verify that what we passed in as dependencies into our persistence coordinator are actually being set as we expect them. If we look back in our persistence coordinator inside the initial Isar, we can see that really just two things are going on here. The model is being assigned and the persistent container is being created internally. We noticed that we're giving it the empty string name because it's not required, it's gonna fall back using the name of the model. So if we kind of think about how this is set up, well, we should have an idea of how to actually write a test. What can we test? Well, we contest that this model matches the model we passed in. We contest that this container is initialized. We can also test that the name of this container is an empty string and that the model that the container contains is the same one that we passed in as the managed object model here. Now, that might seem a little long winded, but once you get the hang of this, it will become easier and more intuitive. Um and so really, what weaken dio is we can do an assertion like this. So x et assert And what are we asserting? Were asserting that the persistence coordinator dot model is going to be the same instance of the model that we passed in. So our manage object model and you'll notice the triple equal sign is the identity operator , not the double equals here. I'm just saying that they assigned the model is the same instance of the manage object model passed in right? So that is basically all that that means. And if we go ahead and run this assertion here, let's make sure that this passes And sure enough, our test passes and let's go ahead and start writing the other surgeons. So, like I said, we have other things we contest, although it might not be obvious. So, Persistence coordinator Don't container dot name that is empty and we know it's empty because we're giving it an empty string here. Now go and run our test and look at that pit past. Now, if I just did something like SDF for the heck of it, let's say I changed and I forgot The benefit of test driven development is when I run myself later on, either in my continuous integration deployment that's gonna be caught right there and fail so something so subtle can be immediately captured by our tests, which is why this is hugely beneficial for a critical code that's gonna be used as a foundational layer in our laps. And for the final assertion here, what we can do is create an ex seats, he assert persistence coordinator dot persistence container dot managed object model is gonna be the same instance of the managed object model that we passed in. And let's go ahead and run all three of these assertions and verified that they pass and there we go. So we've tested pretty much all of the fundamental things that we care about inside of this initialize er here. Now, let's take that same pattern and apply it to the initial Isar. That's just taking in the persistent container. So our second test, what we can do here is create another function called funk. It will be named the similarly test initialize er provided. Now, in this case, we're not passing in. Both the model and the description were just passing in a persistent container, so provided persistent container. And we can expect that this guy's gonna be configured successfully in a general sense, so I can just reuse the name there and set up my test function in a similar fashion. What we can do is persistent Coordinator for the container and will do is create a container. Let persistence container equals N s persistent container. We take a look here at the initial Isar, and we can use the name where we can use the name in the model. I'm going to continue to use the name of the model for now, although it doesn't really matter. The point is that we're just learning how to write tests. Managed object model the one that we loaded from above that we stores are property and the container is gonna be will be passing this the dependency. Now let's go ahead and write our assertions so we can do an X et assert now, although I just did playing assertions here. There's a few different flavors of these. We can do something like this. X ET assert, not nil. If we wanted to check something like Persistent coordinator dot Persistent Container, There's copy from here because the auto complete does not want to work with me today and we can go ahead and run the test. Yes, it does. Here we go. Similar names, Easy toe, easy to mix them up. It's good. Run up. Okay, great. So we know it's not Nil is initialized, and again we can checks a few other things here we can assert the coordinator dot container the model It's the same manage object model that we fed in as the dependency. We can also check in a similar fashion that the model was also initialized assigned because that's something else we care to hold on to, a reference of. So exit e assert coordinator dot model Is he managed object model. We passed in. We go out and run our tests again and verify all three of these assertions pass. And there we go. Now there's one other assertion I want to add on the first initialize er that we created. And I just want to assert that this description is configured as I expected to be. And what I'm gonna do is just simply in X City. Assert that the coordinator dot container the first is the same instance of the description . Now we're passing into the initial Isar and let me go ahead and form at this and let's go ahead and run the test and so you can see that it failed and I go back and I'm like, OK, so what's going on in here? And I see that as I look through here, it looks like I may have forgotten to assign the store description, and this is a great way. Testing can catch little bugs like this if you're not paying attention. So all we need to do is self dot persistent Container. The persistent Store descriptions, which is a collection of descriptions, equals a collection of the store description of a pass in like so and go ahead and run the test again and verified that that passes successfully. Now we've successfully unit tested our initialize er's and verified that they work and they're set up the way we want them to be. The next part of the process is going to be writing the code that constructs our core data . Stack or loads are persistent store descriptions and our manage object model into memory and then writing the unit test to verify that constructing our stack and loading everything is working as we expect. 8. 7 constructing the core data stack: and this. Listen, we're going to learn how to construct our courted A stack or load are persistent store descriptions into memory. And we'll get started by going back into our persistence coordinator here. And we're going to create a new function down here, and we're gonna call it funk Construct Core Data Stack and inside of it we're going to do here is take a look at an important function and that's gonna be on the persistent container here. And there's a function called load Persistent stores. So what we're gonna do is persistent container thought load persistent stories like this, and we'll go ahead and hit. Enter, too auto, complete the closure, and we just call this store description and error and we can delete the unneeded parentheses and let's go and take a look at the documentation for what this method does have gone ahead and pulled up the developer documentation for this. And as we re through it here, it's pretty simple. It instructs the container to load the persistent stores, and as we look down through the discussion section here, there's one critical piece of knowledge that we need to understand, and it's right here that once the completion handler has fired, the stack is fully initialized and ready for use. However, this completion handler will be called once for each persistent store that is created. And since we can have more than one store, we can have multiple, such as one that's a sequel light store and one that's an in memory store type. For example, this is gonna be called for each one of them that is loaded. So when we go back here and look at our code, well, we see that this could potentially be called one or multiple times. And as it stands right now, if we just called construct core data stack. This may not finish in a timely fashion, because this load method might take a little bit more time to complete itself. So we need to come up with some kind of mechanism to let any a P I or any client that's using this method know that the stack is fully initialized and ready for use. So with what we know now, it's obvious that we need a completion handler to be a parameter to this function and call it when everything is finished. So we can create a new property at the top of our class. And what we can do here is start typing out of type Alias and we could just call this set up handler and said it equal to an optional aRer that returns avoid and inside of our constructed or data stack method. Here we can go ahead and do the following completion. Handler had escaping set up handler like that and what we can do in our load Persistent stores method here. Let's just go ahead and call the completion handler directly like this, and we can pass in any error if we want Teoh. And that error comes back from here so that anybody that is checking for an error can see if something failed when we were loading the stack. Now there's still more work to be done here. We haven't considered what happens in the case of an actual exception that gets thrown. Now, the easiest thing to do would be something like this. We can unwrap the error like if let error equals terror, maybe print, and we can go ahead and dump the localized description into the log just so we can see what may have failed. And in our completion handler, we can call it here and pass in the error once again and we could return. So by handling the logic like this, well, we don't need to pass in anything here. So we can change this from error to nil, because if we encounter an exception at any point this return functional, prevent any code below from executing. Now there's still one use case we haven't considered here. And like I said before in the documentation, this could be called multiple times. So we don't just want toe call our completion handler the first time this calls back, it could be called back twice. Or maybe even more so we really want to do is keep track of how many things we need to load . And we can do that by using the persistence containers store descriptions count. Now, basically, what that means is we know how many descriptions this should load into memory, and we can keep track of it like this. Let stores to load equals the persistent container. The internal container thought persistent store descriptions that count. So in the event that it was both in in memory and sequel light descriptions. While then this will yield the value of two. Now we also want to keep another variable loaded stores count, and we could start off with an initial value of zero. So what that means is, each time this could get called, we'll go ahead and increment that local count by one. And we can add some additional logic here around our final completion handler call such as if loaded stories count is equal to stores to load. Then and only then do we call our final completion handler that everything is set up. Now. That's all the good needed to load the stack. Now let's go ahead and write a unit test to verify that we can load our stack successfully so we could go back to our persistence coordinator tests and scroll down at the bottom here , and we'll start with a new function funk test construct core data stack. So where do we interested in in this particular example? Well, we're interested that are completion. Handler gets called when everything have set up, and we can verify that so we can call the last part of the test name here. Completion handler called success. What I'm gonna do here is borrow some code from up above in this first unit test for our initial Isar. And I'm going to copy this description and the initialize er for the coordinator into our new test. And I'm going to then dio for assistance coordinator construct. And we go ahead and type in the error here and now because this is an asynchronous call. We need to tell our unit test to basically wait for it to finish. If we run it right now, the test will finish, probably before this even calls back, which doesn't really mean anything for validating that are test actually works. Ah, And so what we can do here is create something called a test expectation or an ex C test expectation. It looks like this. We call it Let's set up expectation yourself dot an expectation with the string description . We just call this construct stack expectation, and what we can do is there's another function part of the XY test framework called Wait for expectations. With the time out. Now, I can specify the time value of anything, really, but I can give it something more like three seconds just to be reasonable and in this handler, I don't really care about it. So I'm gonna go and pass nil and basically that's going to block the test for three seconds or basically wait for this to call back. Now, how do I notify the test that this is done waiting? What Aiken Dio is do a set up expectation dot Fulfill, which means the expectation has been met inside of the completion handler. And if you run that now, you'll see that the test will pass even though we haven't wrote our assertions just yet, but because it's calling back in the allotted time window of three seconds now, although it appears we might be done with this test, there's a few more things. Weekend right, assertions for to verify completeness, we would probably want to assert that the error is no so we can write Nexia certain ill on this error here. And additionally, what Weaken Dio is we can check that after everything set up, we expect that the loaded store description should match the value here that we configured initially. So what we can say is X et assert persistence coordinator dot container That's store descriptions, the account should equal one. But we can take that a step further. Weaken, copy this line of code here, and we can put it right under there. We can clean up the end here and we can look into the collection of store descriptions and we can just go dot first. And that's going to be the actual description. And we can say that the 1st 1 should directly match will be the same instance of the description here that we passed in. And if we really wanted to take it to the next level, even though that check is sufficient, we could just do something like this and say the first dot type equals the in memory store type because we want to make sure that that's exactly what's being loaded, Nothing else. And now let's run our final test for complete verification that we are constructing our core data stacks successfully. There we go, everything past, and we can see that all of our assertions have been met successfully 9. 8 saving fetching from a context: the next part on our journey through your data is gonna be learning how to save the managed object models that we create and basically per system in memory or to persist them to the backing sequel light store, depending on how our core data stack is configured. Additionally, once we've saved our models, we also need a way to fetch them back into memory from a particular managed object context . And in this video, we're gonna learn how to set that up. We'll get started by creating a new function right below our construct core data stack function and is gonna look like this funk save changes in context, an s managed object context. And we're also gonna provide a completion handler similar to what we did before in the constructed or data SEC method. We'll call it completion Handler. We'll do that escaping there, and we'll go ahead and call this set up handler. But we're going to rename this completion handler just to make it more general usage threat or a P I. Let's scroll back up to the top of this class year and look for that set of Chandler and we'll go ahead and rename this completion handler just for more general usage throughout our A P I. And we can update the two references one being in the construct core data stack method and the other one being in our safe changes method. Now let's go take a look at the Apple Developer documentation. The documentation on the managed object contexts gives a high level overview and some explanation for what to expect. But the section on concurrency is very important. And as we create these data's models, these data structures in our example the folder in the notes we are going to funnel them through a managed object context. And when it comes down to saving, we can create this context to save our records on the threat associated with the man Q or on a context associated with a private, que concurrency type, you could think of it as a background threat where we can save we can fetch, weaken do operations off of the U I thread to keep Are you I light and simple ah, processing data behind the scenes now a general rule for any kind of multi threaded work is in general. You want to keep things as simple as you possibly can, because multi threading anything is complicated to debug. And it's so easy to set up a situation where you're gonna have problems and you're gonna have no idea. I had a really fix it completely. Now when we go back and get the persistent container, if you remember, we have contexts provided by the container already that do some of this work for us, so we're absolutely going to take advantage of this. We have the view context, which is the context associated with EU I thread. It's already configured and ready to go. We don't need to do any additional set up. And if we want to do anything in the background context, we have methods available to do that, we can create a new background context or we can perform a background task inside of a block that gives us a context to save in. Either way, this is great and we don't have to write any code. It's already ready for us to take advantage of now. If we were not using the persistent container and let's say we were creating our own persistent store coordinator from scratch, then we would have toe set up in implements are managed object models as we want to configure the concurrency types. Load the model and all of that stuff, and we don't have to do that because we are taking advantage of this higher level. A p I. Now let's get back to implementing are safe changes method here. The first thing we're gonna do is call context dot perform, and it's gonna give us a block to execute. And if we look at the developer documentation for the perform method it a synchronously performs a given block on the context. Skew. So basically, it's allowing us to save changes to our managed object context off of the main threat in the background thread, or more so if it were using a private you concurrency type. And so that's advantageous to keep the U I light ends not blocked. If we have a large sets accorded operations being executed next, we need to check if the context has changes and we can do that like this. If context that has changes and when we implement that and we look here at the has changes method, it's a Boolean value, indicating whether the context has uncommitted changers whether new manage object models have been added, updated so and so forth, This is going to be toggled on if any changes to the context have been made and then we could save those changes next. So similarly, what we can do is call context dot save and at this point, all changes to the context for any additions updates. Removals we might have made in this context are gonna be saved up to the persistent store coordinator, which will either save them in memory, right them to a sequel light database. If that's how we've configured our stack so on and so forth Now, this can throw an exception. So we need to go ahead and mark this with a try right before it. And that means that we're gonna have to wrap this inside of a do try catch block. So what we can do is set up our code like this, do catch and we can cut this little block here pasted inside of the do block here. And any exceptions that make it triggered will come right down here into our catch block and then we can tell our completion handler Hey, this completed, but there was an error, and we could pass that through If we want to know. Another thing with the managed object context is you can set them up in a parent child configuration where you can have a parents manage object context that could have a child object context below it. So if you make changes to the child context, you want those changes to trickle up to the parent context. And if the parent is the route than the persistent store coordinator sits on top of the parent context. So you call the parent context save method as well, too. Trickle those changes all the way up to the store coordinator. Now you might be thinking, How do we do that? And the property on the manage object context has a parent context. It looks like this context dot parent and we called out, Save on it. Now the thing to note here is there may not be apparent context. This is an optional property, so depending on how we can figure out core data stuck if we set up our context to have a parent child relationship than calling, the safe here will trickle up to the parent and save it there as well. If there is no parent context at all, and our context truly is the root context, then this save will do nothing because parent will be nil and calling. Save here is on an optional property here isn't going to do anything. So we're safe in that scenario. And if we do have that set up in a parent child relationship kind of scenario than this safe here will make sure that the parent context gets called as well. Now, this applies more so outside of the context of the s persistent container. If you were doing things of a more manual fashion, but it's not a bad idea to basically add that in, just in case you switch up your stack in the future. And now we're making sure we're saving the child context and the parent context. If there is one now to knock out the last little bit of functionality here we want to do is after we've gone through this. If check here, we want to make sure we call our completion handler and we could go in passing nil because we're gonna assume that if it didn't throw an error by this point that we have succeeded in our save. And we could just call our completion handler. Likewise, if an exception is thrown, we might want to print it out, it an error and maybe print out the description of it. The inferred error from the catch block exception here. And because our signature of our completion handler passes back in error for anyone who calls it, you might want to know what went wrong. We can just call completion handler and we can pass in the error through it. So anybody using this method will know if something blew up. Let's go ahead and write our fetch method next. So people are safe function, we go funk, and we'll call it Dutch and from context managed object context and a fetch request. And that's going to look like this. And a sketch request and s fetch request result like that and a such completion handler. And we haven't implemented this completion handler, but we're gonna implement it in a second. And so basically, we're passing a context that we want to fetch manage objects from We're providing a fetch request and this basically tells the context what kind of data we want to extract from it. What models we want to pull back. So on and so forth. And the completion antler is just going to pass back any fest results that we get from our context. Let's scroll back up to the top of the file here, and we'll go ahead and create that that results Handler. So similarly, it's what we did with the completion handler. We can start by creating a type alias called Fetched Results Handler and the signature is gonna look like this. So what's going to return void and inside of it here, we're going to have a collection of N s managed objects. Basically, all the objects that we fetch back from the context and the market is optional in case we don't get anything back or we counter an exception and we don't want to pass an empty collection for the argument. The additional parameter for this signature is gonna be just a optional error in case we hit an exception. We can pass that back through. Likewise, how we did with the completion Aylor. Now let's go back down to our method here and finish implementing it. We're gonna start off with a do try catch because calling context that fetch is going to potentially throw an exception. And the reason why the completion handler is used here is because in the event that we catch an exception, we can call our completion handler in that scenario. And even though this is not purely in a synchronous function, the benefit of doing it like this is we can wrap the do try catch inside of this function. If we don't do it that way, we'd have to basically mark this function as throws and let whoever's calling this method handle the exception upstream. And that's totally valid, if that's what you want to dio. But to basically abstracted away into this ap, I will go ahead and let this catch and handle any exceptions and just call the completion handler if that happens. So what we can do next is do you try context that fetch we can pass in the fetch request and what we can do here is let fetched results equals that and fetched results right now has a type of a collection of any so weaken dio his cast this as a collection of N s managed objects because that's what we're gonna be fetching. So at that point, we're good. We can call her completion handler like this. Fetch completion handler, passing the test results and mill for the exception. Likewise, if something goes horribly wrong looking to hear it was prince fit and error localize description and then call our fetch completion handler passing nil for the manage objects. Because presumably we don't want to pass anything or an empty collection in this air in scenario and then passed the error in itself. And that completes the fetch from context method. Now, we've completed our main methods here. But wouldn't it be great if we could verify that these air working without having to build a nap to save and fetch from and we can do exactly that in our unit s just like we did with our set up functionality. So let's ah, take a look at our XY data model again. And what we're gonna do is create a unit test that can create a folder and add a note to the folder, save the objects, and then fetch them again from the context and verify that the saving in the fetching was successful, and this is really great because then when we want to go back and put it into our actual app, we're already going to know that this good works and we have various tests that support it . So let's pop over into our persistence coordinator tests and start setting up a unit test that can save and then fetch again from the context. Before we can start writing our tests, we need to expose the manage object contexts on our coordinator that we're gonna be using here. So let's go back and your persistence coordinator one more time, and we're gonna expose two properties, the 1st 1 we're gonna call the fetching context. It's gonna look like this bar fetching context. I have type Hennis managed object context, and that is going to return the persistence containers view context. And that's the context Associate ID with the man. Que If we wanted to display anything to the user interface, we would use this context now for saving. We're gonna create another bar, and we'll call this private context, and it's also going to be an N s managed object context and in this instance, we're gonna return Persistence container dot knew background context. So every time we're calling private contexts, we're getting a new instance or new background context invented for us automatically. And we're going to use these in our unit tests and throughout her application to save invest. From now, let's go back into our tests here. Let's start off by creating a new unit test and we'll go ahead and call this funk test save changes in private context. Completion Handler called success and we're going to need to do is what I want to do is create a property in the unit test class that could basically have this available for us anywhere we need to use it in some of these tests. Since we're not explicitly testing the construction of the coordinated stack or the initialization, we could go and create a property up top of this file to keep a reference of this so we can use it for other tests. Weaken Just leverage our test constructed or data stack. Here we can copy these three lines of code that initialized the persistence coordinator, and with that copied, we could go all the way to the set up method here, and here's what we can Dio will create a new bar. We just call it coordinator persistence coordinator, and we'll just treat it as an implicit optional. And what we can do, then, is after this has been set up, we'll go ahead and build this coordinator, and we'll just change the name. We're going to give it the in memory store type and the manage object model. And really, after that's been done, well, just assert on our coordinator and make sure that's good. And we can just run our unit tests as a quick sanity check to make sure that this is set up correctly and is available for use. And like I said, this will help us in future unit tests. So we don't have to keep repeating this code for each test and our test passed, so we're good there. Let's complete the rest of the unit test, so the first step in the process is going to be creating are managed object models first, and what that looks like is this. We'll start off with creating a folder because that's the root level structure in our data model. And let's folder equal folder And when we create the manage object, we have to give it a context. Now we can create this one of two ways we can give it an entity description in a context or just give it the context directly. And I'm gonna go with this option here So we'll do coordinator the property we just created earlier and will give it the dot private context as the context we want to create it in. Now we can go ahead and assigned the additional properties on this model, so dot title equals what we can do here is go up here. Let expected folder title equals test folder title and you can name it. Whatever you want is that just test data. It's irrelevant, and we'll go ahead and assign it to the expected folder title and folder. Dot created date equals, and similarly, we can do let expected folder created date. It was just a sign that to the current date for now, just fine and expected folder created date Now, below that, let's go and do the same thing. But for the notes. So let and I might need to cast this as an an estate, which I think I can do like this because I'm giving it the swifts type, but okay, let note you cooks. Note inside of the coordinator Top Private context note dot And so remember, here we have created Data's well title, the full No text and so on and so forth. And so I can do Here is let expected. Note. Title equals test note. Title expected. Note, message, test note, message like so, And we can go in and sign those over right here. So note dot full note will just be the note message. No doubt title equals the expected no title, but expected Note they because the date and we could set it up here. And that's created date so expected. No date casted hasn't an estate there. And let's see if you have anything else in here. Note. Don't full note. Created date, title and folder. Now we're gonna do here. It was because a folder can have one too many notes as we talked about earlier. There are convenience methods on the folder to add a note so we can do folder dot ad two notes, and we can add the note to the folder just like that now we can just do a quick sanity test . Make sure that our unit test is compiling successfully and it looks like it is so that's fine. Now let's go ahead and write the remainder. Parts of Saving and Patrick Now. Next, we need to create a test expectation, because this is going to be in a synchronous function, so we're gonna need to wait for a little bit for it to call back and finish saving and fetching. So let save expectation, equal, self thought expectation. Just call this save expectation like that. Now let's call our save changes So we'll do coordinator that safe changes in coordinator that private context. We can auto fill that completion handler here and for the exception, just call it Save ever like that and we can write the first assertion in a successful save . The exception should be no. So what we can do here is x ET a certain ill save error. Another assertion that you contest for is that when we call save changes, it saves in the background threat, which means it's not on the main threat. There's an A P I that lets us check if we're on the main threat or not. So we can write in X city, assert false thread that current thought is main threat. If we accidentally change that later on in the future and we still have this assertion in place, this will help us catch that change. And then let us know that we may have changed something that we didn't want to change accidentally. Now, uh, the coordinator that fetch is what we're gonna call next. After we've saved our changes. We want to fetch again to verify that Are objects saved exactly as we expect them and match everything we defined up here. So because we're inside of a closure here inside, if they have changes, rather gonna have to call itself dot coordinator or a week self. If you constructed that way like this, a self taught coordinator that fetch from self dot coordinator that private context and then we have to give a fetch request. Now for the managed object models. They have a class melt that built in that constructs a fetch request automatically and because we want to fetch a folder because of folders, the parents that contains the notes will end up doing folder dot fetch request and it builds a fetch request exactly for folders, and we can give it that directly. So it's quite easy. We can auto fill the completion handler like this for the collection of managed objects. We can just call this fetched results for the exception. We just call it, fetched her fetch error and clean up the formatting a little bit. Here and now, we're all set to start writing the rest of our assertions down here. We can x et assert, not know the fetched results because that should be valid. We can assert nil on the fetch error because we're testing the happy path here. So that should be good there next weekend. Assert that he fetched results. That count equals one because we only created one folder and we're gonna have to get the note from the folder separately. We can also assert that he fetched results is a collection of notes, Europe's aria folders. Because the fact results is just a collection of manage objects. We don't necessarily know what type of object we're getting. It's just a general, a collection of that any of them, so we can inject that and Now what we can do here is actually get the folder so we can do Here is let folders equal fetched results a za collection of folders like that we can get the folder equals folders that first because we only have one here. So that's a valid cheque in this scenario. And then we can read our for a set of assertion so x e t assert no our folder and then weaken Dio X Et Assert equal folder dot title to the expected folder Title and Exit E Assert Equal folder dot created date to the expected folder created date, and we can also do another check here. Exit assert folder dot newts. That count equals one, and that completes the first set of assertions on the folder. Next, we'll need to go ahead and get the notes. Now let me just do a cast here on an estate because that's a date and a state comparison there, and let's continue forth with the final assertions on the notes. We can get all of our notes like this, but notes equals folder dot notes, and that's a set to get back the collection or the array of the items instead a little bit easier to operate on for the test. We can cast that as from in any to a collection of notes like that. And since it's optional, I'm going to do it inside oven if let here and inside of the F let will just to a four loop for Newt in notes and liberate one time and then we can do here is copy one of these exit es or equals and set up our sessions. Just have that in now for this one, it's gonna be note dot title to the expected no title, and we just do that again. It's copy this one more time. Thought full note to the expected note message, and we can do another one for the date. So no dot created date to the expected date. Remember, as an estate, otherwise you're gonna warning there are compile error. And now we can also make sure that the Notes folder is actually the folder appear that were getting the notes from so we can do it like this. We could do an X et assert, not know, no folder, and we can take it one step further by doing an X et assert note. Thought folder is the same instance of the folder above, and we can verify that that is in place there. And finally, because if you look at the manage object, you'll know that it has a property for the manage object context. If we really wanted toe, take it to an extreme er level, we could do this exit. Yes, sir. Note that managed object context is the same instance of the self stop coordinator private context. That's where it should have come from. Additionally, Weaken apply that same assertion to the folder right above it here, and we'll just change from note to a folder instead. And there we go. Now, we're almost ready to run this and test everything. What we need to do here is to more things we need to await the expectation. So save expectation. By the time we get through here and we've completed this will call save expectation, top fulfill, and outside of this whole safe changes block right here, we need to actually wait for it. So wait for expectation. With time out, give it to you that two seconds so 20 should be fine and nil for the other handler there. And now the one final thing we need to do before we contest this is actually called the construct Core data stack method on that coordinator property we made above. And one thing I want to change before we write that construct method is I want actually keep the saving context in a property. So I want to do this. Saving context equals coordinator top private context. And the reason is because if you remember, when we set this up, let's see if we can go into that method. Um, since coordinator, every time we access this is gonna give us a new context, and we want to keep all of this in the same context. Otherwise, it's gonna invalidate our tests. So that's no worries. That we can do here is just keep one reference to it and just update it inside of the note and folder initialize er's. We can also update it here and fetch. We can update it in the save here and really quickly. Right here on the folder check and on the note check. Right there. Coordinator. Dr. Private Context. Okay, All instances are fixed. Now, let's go all the way back up to our set up function here and just write our construct methods so we can do is create another assertion. Let's construct expectation yourself that expectation with string, and we can call coordinator that construct construct error. We can do, assert no on the construct error and then fulfill the expectation. And we can't wait for that expectation in the set up method. Give it a two second time out arbitrary number, but that should be plenty of time. And now let's go ahead and run our unit tests and verify that everything passes now for the moment of truth. Let's run our unit test and there we go. Looks like everything passed and there's a lot going on in this unit test. So, you know, you've got to make sure that you didn't make a mistake anywhere along the way. But that wraps up the saving and fetching functionality In the next video. We're going to implement the delete functionality so that we can delete manage objects from a context save it. Persistence changes so on and so forth 10. 9 deleting from a context: So far, we've got a pretty good grasp on creating managed objects and fetching managed objects. But we need a mechanism to delete managed objects from a context. If we want to delete records and persist those changes, we need the functionality to do that, and that's we're going to implement in this video. So let's go back into the persistence coordinator and right down below are safe changes in our fetch. What we can do is we can create a new function. We're gonna call this funk, delete managed objects, and it's gonna be a collection of Ennis managed objects like that. We'll also go ahead and add a completion handler, similar to what we did in previous calls so we can do a comma completion handler and that will outline our basic delete function before we write the rest of our delete implementation. I want to talk about a concept known as the Delete Rule and how it's going to apply to our folder and notes. Example. So to talk about that, let's go over into the core data notes up XY Data Model file. Select the folder managed object. The relationship notes. If you go into the afternoon inspector here or the data model inspector, you'll see that there's a delete rule and it's set to nullify. And if you expand the drop down, you'll see that there's four types. No action nullify, which is default, cascade and deny. And what I want to do is briefly talk about delete rules so you can understand how to use them. Because depending on your app in what you need to do under different circumstances, you may need to change the delete rule from nullified to something else. The Apple developer documentation under the relationship Delete Rules section doesn't pretty good job of explaining the differences between the four types that we saw in that drop down in the example that they use. They consider the department that has many employees, which is very similar to a folder that has multiple notes, as in our use case. Now there's the deny, the nullify, the cascade, and then there is no action. So what I want to do here is go back Teoh our data model, and consider how these four different options are going to impact the relationships between our managed objects. If we delete a folder that might have notes already in it. Now, let's kind of consider this in the context of what we're doing here. Starting with nullify the Apple developer Doc says that it removes the relationship between the objects, but it does not necessarily delete the object. Now we're going to consider this from the use case of deleting a folder that happens to have notes already inside of it. And so if nullify happens to be our delete rule on, what's gonna happen is if we delete a folder that has notes, the folder itself will be deleted. The relationship from the note back to the folder will be broken. These notes will essentially still exist, but they're not gonna have a parent folder. This gives us an opportunity to maybe reassign our relationship to give these notes a new folder. If we wanted to move them from one folder to another folder, and that's totally valid and useful. Now let's consider if we select Cascade. So if I go to the drop down and I changed the delete role to Cascade on the folder, let's go and read here. It deletes the objects at the destination of the relationship when you delete the source. For example, if you delete a department fire all the employees in the department at the same time. So if I deleted a folder in Cascade is our delete rule than every note on the other side will be deleted as well, essentially wiping out the entire folder and everything inside of it. You have to think very carefully about using Cascade. It could be dangerous in certain circumstances, but for our application that we're gonna build Cascade is gonna be what we're gonna use because we just want to simply delete a folder and see that all the notes inside of it are also gone. But you'll want to consider this on a case by case basis in your app, especially for your data integrity rules. Now let's take a look at deny. Deny says that if there is at least one object at the relationship destination, employees do not delete the source object. So as long as a folder has no since side of it, then I can't delete the folder I have toe basically wait until this folder has no notes left inside of it in order to delete it. And so this actually maybe more protective, then something like Cascade, which is just gonna do if you delete the source than everything at the other end of the relationship is blown away with it now for the final delete rule here is called No Action, which is just do nothing to the object at the destination of the relationship. So I could delete the folder, which is the source, and the note is still going to think it has a relationship back to that folder that I just deleted. Now, Apple Kenneth says here that there may be some use cases for why you might want to do that , but that's probably something most people are not going to ever have to use in production. So you're really gonna want to focus on Cascade, nullify and deny, and would nullify being the default. Usually that's good enough for anything you need to do. But as I've said before, I'm using cascades simply to illustrate that we can delete an entire folder and everything at the other side. But you wanna conscripts, but you'll want to consider this carefully before you make a selection in your application . So what? That overview explained, Let's go back into the code and finish implementing our delete function. We passed in a collection of managed objects and essentially, we're going to loop through each managed object in this collection and delete them one by one. Now, there is such a thing as a batch delete in core data, which operates and pretty much a similar fashion is what we're doing. But to keep things simple and because this is kind of an entry level app that's gonna exercise core data and it's not something super advanced. We're not gonna go with the batch. Delete. Rather, we're going to go with an iterative delete such as this, and it gets started with that. We're going to create a four loop for I in zero, the manage objects that count and we're gonna iterate through. And the next thing we need to do is get a reference to the managed object context for each object that were about to delete. It turns out that every managed object has a property called manage object context, and it looks like this, and this is how we access it. If let context equals managed objects at the IIHF index dot manage object context, then we'll use this context to delete the manage object from. And if we take a look at the documentation here, it's really just the context with which the managed object is registered. So it makes it easy for us to use this to get access to the context rather than having to pass in the view context or maybe a background context. We can just try to query it directly from the managed object directly. Next, we'll go ahead and actually delete the object from the context, and that's really simple. We could just do context, got to eat, and that's gonna be the managed object at the AIF index. And let's take a look at the delete function in the Apple Developer documentation and delete a simple it specifies an object that should be removed from its persistent store when changes are committed. And this is important when changes are committed because in a similar fashion to how we had created a manage object in a context, we had to call the save changes to make sure that the object was persisted in the persistent store. Likewise, here will need to make sure we call save changes to make sure that this object is deleted from the context and ultimately removed from the persistent store To save the changes, we can simply do context that safe changes. And remember, this is a throwing function. So that means we're gonna have to do a do try catch block so we could start off with the try a context that save and they try context dot parent, not safe. And if you remember the parents is the optional parents manage object context. If we have a parent child manage object context relationship, you're allowed to nest. Ease together. So if we had structured our core data stack in such a way, and we wanted to save those changes up to the parents, we can call the parents safe here. And if the parents is no, there's no harm because we're optionally chaining it. It's not going to do anything now to solve this problem here. Rather than marking this whole function ist throws, we can wrap the loop and they do catch block like this. So do catch and the catch block weaken due completion handler and passed back the error, and we can move this whole four loop inside of our do catch block in here. It was clean up the formatting a little bit. And finally, once this loop is executed in a successful scenario and there's no exceptions that were thrown, we'll just call completion handler at the end passing nil because no exception was thrown and we're done. Now let's pop over into our unit test file, and what we're gonna do is we're going to write a unit test for this, and we're gonna generates a manage objects, save them and then delete them from a context, fetch them again and verify that all of our records have been successfully deleted from the context. Let's head back over into the persistence coordinator tests and scroll down to the bottom and creating new function. And we'll just call this test delete managed objects for the name so and let's go ahead and start implementing this test now, in order to actually delete any objects we need to generate some manage object models to delete. And what we're going to do here is create an extension on our tests here just to kind of segregate our actual tests from anything that's going to generate test data or test content . That's what I can do here is create an extension, and that extension is going to be on persistence coordinator tests. And if you want to move it into a separate file, that's totally fine. For the sake of simplicity, I'm gonna leave it in this file for the tutorial, and we'll do here is create a function and we just call it Funk generate. No, it's we're gonna pass in a context. So context like that and another parameter is going to be amount, which is gonna be the integer, and we're going to generate X amount of note managed objects in the provided context. Next will create a simple for loop here, and Rita iterated through the amounts of four I and zero less than the amount we'll create a new note. So let note equals a note created inside of the context parameter that were passing in note that created eight equals. We were going to use an estate because it's gonna make us cast may see how we did it from here. Yeah, we just doing an estate. No, that title and the reason I'm doing this is thes are arbitrary values, so I could just assign them to whatever the value of eyes at the time in here. And the most important part, though, is going to be making sure that we save them. So I'm gonna do a do catch block here and free generation try context, that save. And if anything blows up, will fail the test, maybe pass in the error or the exception message so we can see what went wrong. And that's it. So now we can loop through and generate a bunch of notes. Now let's go ahead and fetch these notes and make sure that we're getting back what we actually generate here. Now, back in our delete bandage objects method in here, let's go ahead and define amount we want to generate. Let's give it. Ah, large number. So let amount to generate. He was 3000 and we'll go ahead and find the context we want to use. So I'm just gonna do let context equals coordinator private context. And this context we're gonna do the saving, the fetching, the deleting and the such again off of when everything's done. Next, we'll go ahead and call generate notes. We'll pass in the context and the amount to generate. So far, so good. Now what we can do next is fetch back the results so we can do coordinator that fetch, go ahead and pass in the context that we've been using here, in the local context we defined and we'll go ahead and pass in the fetch request. In this case, I'm going to do note dot net request and we'll go ahead and implement the completion handler like this that results and fetch ever or fetch exception. We can go ahead and read assertion that the fetch exception is now so we can do an X et assert No on fetch error like that. And then we can check for our fetched results and make sure that they are what we expect them to be. So if let results, we can check the count equals the amount to generate, then we can go ahead and write our delete. Let's also make sure to handle the else case here. So, uh, else if we could not fetch results and the count is not, we expect we can just fail the test and then inside of the Affleck block, what we can do is call coordinator that elite and the managed object collection is going to be the fetched results, which is what we fetched back. So I'm gonna delete it, delete all of them, and we'll go ahead and implement the completion handler in here. It was called this fetch error liked up and inside of the delete block Here, we can dio is x et a certain ill on the fetch error. And really, at this point, there's only once more step. We kind of want to fetch again, uh, all of the notes. And if our delete worked, then when we fetch from the context we just deleted from the return count should be zero. Now, let's go ahead and fetch again from our coordinator North touch From the context we defend above at the top. The Fed request is gonna look similar to snoot dot fetch request and we'll go ahead and implement the handler here. Fetched results, error as they must be identical to the first factory did and self, because it's already inside of ah, the closure in here. And let's go ahead and do that here too, and will exceed a certain l on the fetch error. Like so and we'll assert that the fetch results that count is equal to zero and is one other thing we're gonna need here because this delete although it's not a synchronous if you remember when we implemented it, it's simply just looping through. But because it can throw an exception, we decided to create a completion handler just too easily call back in these particular cases. So what we can do is add a test expectation, so let to eat. Expectation Looks like that. And what we can do is kind of right after our final check here for set thes are the Countess at zero. We can just go ahead and fulfill the expectation. So delete duffel, Phil, and we need to make sure that we wait for it at the very bottom here. So remember, we have a fetch a delete, and then a fetch again. So we want to do is place that on the outside of the first sketch that we make so we'll just call wait for expectations. With time out, you could given arbitrary value. I'll just say, wait three seconds and I'm gonna give no for the handler and so we can wait for our test there. Now the moment of truth. Let's go ahead and run our tests and verify that everything is working as expected. And arguably some of the stuff minute be completely necessary, especially the fetch again. But it's really just for good measure to make sure that for delete worked, we shouldn't be able to fetch back anything that we have just deleted from a particular context. And as you go through this, make sure that you don't accidentally create a new context. So maybe you save your objects and one and then you accidentally fetched from another one on your tests fail. You want to make sure that you are creating, fetching and deleting all from the same context. Eso something to keep in mind when you're implementing this in your code. 11. 10 configuring the persistence coordinator: and all of the previous videos. We focused on building the core functionality for our persistence layer. And with the majority of that work done, we're now at a point where we can start building our application that's going to create and save folders and create and save notes and actually plug in the foundational layer that we've been writing all the way up until now. If you remember back when we were writing our unit tests, we wrote test to configure and initialize our persistence coordinator. And we're gonna actually need to do that inside of our application. And we're going to do that in the APP delegate, since it's truly the starting point of our entire application flow. And what we're gonna do is we're gonna implement that logic and the did finish launching with options method simply because it is the entry point into the app, and it's what we want to happen before anything else. So what we'll do is we're going to go to the top of the file here and import core data, and we were doing this because we're gonna be utilizing some core data AP eyes in part of our set up routine, and we're gonna need to make sure we import these AP s to do that. So after that, we're going to scroll to the bottom here. We're going to create a new function figure for assistance coordinator, and we're gonna call that from the did finish launching with options method here. And in addition to that, we're going to create a new property that's going to sit in the APP delegate. And that's gonna be the persistence coordinator property itself. And we're gonna keep a reference to it like this as an implicit optional. That way, once we've set it up, if anywhere else in the application needs a reference to it, it will always be available in the APP delegates on the up delegate acts pretty much like a singleton. So for some root level things, we can access it directly from there. If we choose to do so Now, let's go ahead and start implementing our configure persistence coordinator method. The first thing we're gonna do is create the persistent store description. Let description equals N s persistent store description description that type N s sq light store store type. Then we need to get a reference to the application bundle. And we can do that like this. Let bundle equals bundle for class. And in this instance, will doapp delegate that self and that will figure out what bundle to give back. Because if you remember what we did this already in our unit tests. But we're trying to basically get the core data notes APP model here. Ah, and so that lives inside of the application bundle in this instance, coordinated notes app on. And that's why we need to go ahead and get that bundle, too. Figure out where that model is located. Next, we're gonna do a guard check, Sogard Let you are l equals bundle WRL for resource. We're going to use the string with the string extension. So if you remember the core data notes, app that exceed data model is the name of the model. And so that's what we're looking up for. The resource name. So we'll just do core data notes up, and that extension is M. O M. D. We also need an additional check because we're going to try to construct the model from the U. R. L. So let model equal N s managed object model for the contents of the URL and the girl is gonna be the one we just built previously. So you are ill and we'll put this on a separate line for readability. Felts If we couldn't get either those will return from this function and printer error message failed to construct the managed object model. And it might even be worthwhile to throw an assertion for a fatal error here. Because if you can't set this up, then you may as well just you crashed the APP because it's not even supposed to get further than this if it can configure the stack. So once we've got that set up, we're gonna quality persistence, Coordinator, We're gonna sign that to a new instance of persistence. Coordinator, we're gonna pass in the model and store description, and at this point, we have now initialized the coordinator. We have assigned it back to our implicit optional at the top of the file here, So we have ah reference to it if we ever needed in the future. Next, we need to go ahead and actually construct the stack. Constructing the stack is fairly straightforward. We can just do persistence coordinator construct accorded a stack, we can implement a handler construct exception, and we could do a check if let construct. Exception equals the construct exception Print failed to construct the core data stack. We can just print out the localize description, but you could probably throw an exception air their fatal error there if something here happened because we also don't want to advance. If we could not construct the stack, we want to make sure that we're also setting the URL for where the database is gonna be physically stored on the device. Now, because we're going with this approach here, creating the persistent store description manually and assigning it this equal light store type. There's an additional property called description that Earl, which is the actual location for where the file is going to be stored. And so if we go and do this approach where we implement it manually, we can control where we want the actual data base to be stored, and your typical locations are going to be your documents directory in your application support directory. And if you just use the and as persistent container automatically, it manages that for you, and it lets you put it, I think, by default in the application support directory. So if you had to need to know how it worked or wanted to do it manually, it's gonna look something like this. We first get a reference for the application sport. You are. You are l like this not shared by default that your URLs for application sport directory in the user domain and we'll get the 1st 1 back and then we're going to do is create our database path like that. So let database path equals the applications for your URL that a pending path component and this is gonna be the name of the file itself. And in our case, So I'm just gonna use the name database, and so are sequel light file is literally gonna be called database, and all we need to do then is a sign that too, the description wrl now for an in memory store type like we had in the unit tests, you don't need to set this. This could be nil because everything's held in memory, but for actually persisting something to a file, you have to make sure that you set this otherwise your actual data won't be persisted to a written file and just to show by example, where this file physically lives on the simulator. If you recall, you know the app we install is a sandbox container on this simulated environment. And there's this free tool called Open Sim and you can find a get, huh? Blink for it. And it basically gives you access to the simulator sandbox path so you can quickly go and reveal sandbox and finder and go right to the particular simulator you're looking at and see the physical folders inside of it. There's another tool called sim folders, which does the same thing, but that costs, I think, $10 or it costs a little bit of money. But this is the free counterpart to it. And once you have access to your simulated sandbox, you can see that there's a documents folder, a library folder, a system data folder in a temp folder. And typically you're going to save your actual physical database in the documents of the library folders. The temp folder can get purged by the system, so it's not a good place to put something that you need to actually persist on a device. And if we go and look in the library folder here, we have caches, preferences and applications support, not typically, when you do things and user defaults, they go in the preferences folder. But because we specified applications support. If you go inside of it here, you'll see that there three physical files, all of them named database. And that's exactly because I told it to go to the applications, support your URL and to use the name database for the file. Now you can still go back, and you can change the URL from application sports, maybe the documents directory and save your database here if you choose to do that. But you want to be careful to avoid using the temp directory because this can nuke your entire database. So now our databases set up and good to go and our persistence coordinator AP eyes are written, so we have all the functionality to create, save and delete. And in the next video, we're gonna actually started building out the application with all of the hard work we've put into now building out our persistence coordinator 12. 11 creating and saving folders: let's navigate over to the man's story aboard. And the view controller that is in there is gonna be our launch point for building out the user interface. And the first thing we're going to do here is Ah, What we're gonna basically structure is this is gonna be more or less a table view of folders, and we're gonna have a button to basically create new folders. And when we tap on any particular folder, we're gonna be able to go into that folder and then look at the notes and create notes so on and so forth. But we're going to start at the top level first, which is the folders. And the first thing we're gonna want to do here is put this inside of a navigation controller because that's where we're gonna put the button for creating new folders. So to do that, you can select your view controller, go to editor, go to embed in navigation controller. And once that set up here, we're gonna also go ahead and change this name of this view controller. So I have you controller that swift and my project here. I'm going to just go ahead and delete this file. Move to trash and I'm gonna go back to core data notes app The root folder. Create a new file Cocoa touch class. Make sure it's a subclass of you. I view controller. We'll just call this folder you controller, and make sure your languages and swift and go ahead and hit, creating safe. Now let's go back to the storyboard and add in Are you elements here? So the first thing we need to do is select the view controller and go over and make sure your right toolbar is expanded here. Let's go to the class for the identity Inspector and change the class from view controller to folder view controller. And once we've got that in place will go up here and we're going to look for a U I table view and dragged that onto the storyboard right onto our view controller. And I'm just gonna make mind fit flush to the edges. And what we can do is we can add some constraints dependent to all the sides so you can open up the constraints down here for add new constraints and do a leading a top eight trailing in a bottom all set to zero and then I'll go take care of that. And now what we want to do is we want to add a button to our navigation bar, and to do that, we can go back into. Are you my library? Look for a button and that we actually want a bar button item because that's meant for navigation bars in your eye tool bars. I mean, dragged that in here and what we can do for that instead of the item, we could go over to the street inspector and let's see, here we have an ad option here by defaulted. Set the custom, but you can change to add, and I will give you the little blue plus icon here, representing, adding, Now let's go ahead and hook up our I B outlets to our view controller. But actually, before we do that, what's at a stable view? Sell to our table view. So go back to the library, look for a table view and you'll see table of you sell dragged that onto the table view like that. We can go ahead and give this a re use identifier. I'm just gonna call my and sell just for the sake of simplicity. And now let's go open. The assistant editor here, which is this little middle icon? Um, up here. Make sure you are view controller your folder view controllers on one side. And what will dio is create some outlets. So starting with the table view, select your table view. Hold down the control key, click and drag and that will create an outlet connection. We'll just call this table view hit Connect. Now this similar we're gonna create an I B action for our button here. And so hold down the control key with the button selected click and drag. And that should be an action. Make sure action, not outlet, is what selected. And we'll just call this create new folder and connect it, and I'm gonna go ahead and delete this Ah, prepare for Segway code that's added by default here, our next course of attack is going to be creating the folder after selecting the plus icon here. And the way we're gonna handle that is when you select the plus icon here, we're going to present a u I alert controller that's gonna take some input from the user, which is going to be the folder name. And then once the user hits save, we're gonna go ahead and create a new folder and save it in our persistence store, and we'll start here first. And then once we have that wired up, we'll go back and implement the table view for fetching the folders that have been saved in the persistence layer. To do that will create a new function right here. We'll just call it display alert you and we'll call this function whenever we tap on the creating your folder button. Now inside of the display alert view function itself. We're going to be rendering au Eller controller. So let's alert input of you equals you. Isler Controller. We'll do the overload for the title message. Preferred style. For the title, it'll be folder name. Enter a folder name and dot alert for the preferred style. Now we need still need to add in action here, but what we'll do is we'll call the logic to present it so we'll call it present. Alert, Input view, animated. True completion handler can be no. Now we need to go ahead and add in our buttons. So what we can do here is put in an action so we can do alert input of you that ad action. It will create a you Ellard action. We'll call this action save. Give it the default for the style and we'll go ahead and implement the handler. And for the parameter, we'll just call it action or alert Action. Now we're gonna add one more action for the cancel action. So alert and put view that that action again, you I alert action for the title and this will be cancelled. Style will be cancelled, not default. Make sure cancel selected. And the handler for this one could be nil because we don't really care what happens when they cancel it. We only care when they save it. At this point, if you run the app, we don't actually have a way to gather the input from the user. Yet we just have the safe cancel in the titles here. And so what we need to do is add a text field to our alert controller to take user input from, and we can do that pretty easily. Weaken, go here to alert and put view that ad text field and we'll go ahead and implement. Handler, call that text field and inside of the handle. What I'm gonna do is that the placeholder string I'm going to set it to enter a folder name . So I'm just gonna copy what we had from here. And just to text field that placeholder equals enter a folder name. And so this is gonna put the form that they can type into now to get whatever they type into the form we need to get that once they hit the save button so we can access it here. And the way that's gonna look is like this if let folder name equals alert and put view, or their controller the text fields, which is a collection of text fields that this you controller can support. In our case, we're just gonna go for the 1st 1 because we're only adding one, and we're going to look for the dot text for the string. And we also want to make sure that the folder name is not empty. So we're gonna do an additional check. So not folder dot is empty your folder name and inside of here, we're gonna take the folder name pass it off to another function that's going to create and save the folder in our persistence layer. And so we're gonna need another function to do that. What we're gonna do here inside of our handler is create a week reference to self and that's gonna look like this. So in the event that self is nil or, um, something weird happens, we're not gonna potentially create any kind of retain cycles or have any kind of crashes, and we'll create another function below display. Lor view called Funk create folder folder. Name will be the parameter is gonna be a string. We're gonna call that from inside of this if let check. So we're gonna use our weak self. So self question mark dot Create folder and we'll pass in the folder name that the user had entered into the form and weaken Add announce case to this. We want Teoh. Um, if the folder name was no invalid folder name input to cover that scenario there and at this point, we're ready to create and save thief older for the folder named Passed in here. But we have a little bit of a dilemma. We don't really have the persistence coordinator available to this view controller. And so the obvious question is, how do we provide the persistence coordinator to the full interview controller? And what we can dio is go back into the APP delegate and scroll up towards the did finish launching with options and right after we go ahead and configure the persistence coordinator, we're going to want to get access to our folder of you controller and essentially pass the reference to the persistence coordinator from the APP delegate to the full interview controller. Now, alternatively, we could just ask the app delegate from anywhere in our code base for a reference to the persistence Coordinator because the APP delegate is basically a singleton. But that gets pretty messy. So if we can cleanly pass this along, I'd rather go for that approach. And what this is gonna look like is this. So we know if we go to our storyboard here when we look at the root of you controller, it's actually a navigation controller, and the navigation controller manages the folder view controller and it only has one view controller right now. So with that information, we know we can get the navigation controller in the APP delegate. From here we can get the first view controller, which is the folder view controller, and we can create a property on the folder view controller for the persistence coordinator and essentially do an assignment to it right from the APP delegate. And I know that, my son confusing. But it's relatively simple. And for the first step, what Will Dio is Go to the folder view controller. It will create an implicit optional var persistence coordinator like that and then we'll go back to the APP delegate and we need to get a reference to that navigation controller I was talking about. And so it's gonna look like this. Let parent navigation controller equals the window. That route view controller as you I navigation controller. Now why does this work? This works because of the APP. Delegate has a window property and the window is like the main window of the application. By default, the window has to have a review controller to present any content in and coincidentally because of how we set our navigation controller in the storyboard. The knave controller is the root so we can get that right here and then what we can do is get our folder view controller from the naff controller. So if let folder, few Controller equals parent navigation controller dot view Controllers, which is the collection of you controllers that the knave controller manages, got first because we only have one and will cast it as the folder view controller. And then once we have a reference to it, we can do this folder view controller. That persistence equals the persistence coordinator from the AP delegate. Let's go back into the fold of you controller and actually create and save the folder. Now to create our folder will do this. Let's saving context equals the persistence coordinator that we just added to this glass dot fetching context. And we're going to use the fetching context simply because this is gonna be mostly tied with the user interface or the main thread. So it won't really make a big difference here. Which one we use but folder equals folder created in the context of the saving context folder dot Title equals thief Older name passed into the Method folder that created date equals the date or the current date, and we'll have to cast that as an estate, and finally, we just need to go ahead and save the changes. So, persistence coordinator that save changes in the context will save that in the saving context it will implement Handler, and we can just do a ah if save exception what you call the air save exception. If it's no print, successfully saved the name of the folder and now we can go ahead and run this and verify that we're seeing this print statement being executed. Okay, now, for the moment of truth, But the plus button here and what is going to call this test folder and hit save and we can see in the log here successfully saved test folder. So this completes the first part, which is creating and saving the folder. Obviously, we want to see the results in our table view being loaded back in the next video. We're going to focus on loading and fetching are folders from the persistent store and rendering them in our table view so we can add and visually see the changes as we create them 13. 12 intro to the nsfetched results controller: if you remember from our last video, we were able to save folders and persist them. But we need now way to render them back into our list, and there are really two ways to go about doing this. The first way is we can go through our persistence coordinator. We obviously have methods for fetching from a context and weaken manually. Fetch all of the folders and display them back inside of our view controllers table view. And while that's fine, there's actually a better, more efficient A p I that we can use to achieve the same thing. And it plugs in really well with table views. The A p I that I'm referring to is known as the N s fetched results controller. And as the definition states here, it's controller that you used to manage the results of a coordinated fetch request and display data to the user. And it's really a kind of a glue between core data and user interface, such as a table view. It's really designed to kind of seamlessly fetch data plug into a table of you. It provides a delegate that gives you some opportunities to update your table view delete, insert update rose so on and so forth. So it's very handy in these kinds of situations with, like, a master detail kind of list, which is exactly what our folder notes app is. It's a master detail convention and how we structured the data. And so we're gonna take advantage of that and used the fest results. Controller for fetching are folders back to our table view the first thing we'll need to do to get started integrating the first results. Controller is creating a property for it in our folder view controller so we could go to the top and go up where our properties are and start creating a new property like this far folder touched Results controller of type N s fetched results controller and we're gonna do is give it the type of managed object we're expecting it to return, and in this instance, it's going to look like this. What this means is billed me affect results controller that venz or returns folder managed objects. Now let's go ahead and create a new function called load folders, and that's gonna be like this hunk load folders and we'll call that in view. Did load and inside of this function, we're going to initialize are fetched. Results Controller. Now there's two parts that go into this. We need to construct our fetch request and are fetched. Requests needs to have sort descriptors. This is a requirement for using the test results. Controller. The good news is we have some meaningful attributes Weaken sort by in our case will sort by the creation date of the folder. So we consort either ascending or descending by the date the folder was created by. So let's get started with the metric west first, let folder fetch request of tight End s that request for folders equals folder dot fetch request. Now, we'll send the sort descriptors folder fetch request that sort descriptors equals a collection of sort descriptors. So we'll do an N s sort descriptor for key and descending. Now, the key is gonna be the property in our case created date, and we'll do ascending set to true. And in case you have forgotten or are wondering where this comes from, if you commands click on the folder managed object. Actually, it's the folder plus core data class properties. We're really using this created date for our sorting. Also, we could sort it by title as well if we truly wanted to do that. Now let's go back and continue with our descriptors here. And in case you're wondering what in any sort descriptor is because we haven't really used those yet. It's just basically a way to sort a particular field by and you can support more than one sort descriptor. So that's why this is the collection. We could sort it by created date as well as other attributes. But in our instance, we're just gonna is one sort descriptor for this. Next, we can initialize the controller fetched results Controller equals and s fetched results controller. And this is where we give it the folder fetch request. The context that we're gonna use in this case will use the persistence coordinator dot fetching context for the section name, key path and the cash name. We're gonna pass in nil because we're not gonna be using those. Finally, we're going to assign the delegate to self. Now there is a delegate that I'll mention in just a minute, but let's go ahead and set up the assignment. Todd delegate equals self, and this is gonna be a compile error until we conform Folder view controller to this protocol. Now, let's go ahead and actually perform the fetch. Now, to do this, we need to execute it inside of a do track catch block. Because this can throw an exception. So do catch. And what we'll do is in the exceptional print, the exception dot localized description and inside of the do block will do try folder fetched results controller dot perform fetch. And since this throws the exception, if anything goes wrong, we'll catch it here. And what this actually does is it executes the fetch request and the sort descriptors on that fetch request that we set up. It basically gets his back folders sorted by creation date. And then what we'll do is in the delegate. Here is where we're going to get notified for these changes so that we can insert remove an update rose that feed into our table view. Now, let's go ahead and take care of this delegate here so we can scroll down at the bottom of our folder view, controller, handle implements and extension on folder view controller that conforms to the N s fetched results. Controller, delegate. Now there's really three methods in here that we're going to implement, and we'll start off with the 1st 1 Controller will change content and we'll go ahead and do the 2nd 1 controller did change content and these methods air invoked before and after content is changed that is loaded into our table view. In essence, if something changes in our folders data source either F older was added or removed, then these methods here are gonna be called, which gives you an opportunity to do anything for your table view for how you present things. Typically, what we'll do in these kinds of situations is tell our table view to get ready to start updating or end updating. And it looks like this tough begin updates and in the did change content table view, Todd End updates. Now, before I get into exactly how these work, we need to understand these methods in context. There's one other method that we need to implement that is the cornerstone for this entire delegate, and that is this start typing controller did change, and it's a super long function signature. But let's let's format this a little bit easier to read. So that method is controller did change an object at an index path for a type and an optional new index path. Now it might seem like there's a lot of things going on in here, but it's simple once you break it down. So any time something actually changes on our fetched results Controller, this is going to be invoked with the object that was changed in the index path. It was changed at for the type of change that it is. And that type of change will be either an insert, a delete, a move for an update and a new index path in case we wanted to move something from one position to another. So the really key thing is we want to do a switch on the actual fetched results change type . And so we do that like this switch type, and we want to start looking for those four cases. So there is a case that insert there is a case dot delete. There's a case, not update a que stop move, and that is going to be in unknown default, which we have to implement, has it right now in this version of Swift. The default case has been called this format this little bit. And now let's go ahead and start implementing these one by one. Starting off with the insert any time we create a new folder than the type of change is going to be an insert, and we're gonna have a new index path of value to insert a folder at And so that's gonna look like this. We're gonna check for the new index paso if let new index path equals new index path table of you that insert roseate the collection of the new index path. And you can apply an animation for your inserts. So, out of the box, you have automatic bottom fade left middle None right on top. I was going to use a fade animation here, but you can use whatever you want in your project. Now for the delete again. If we were to swiping delete entire folder, then it be changed type will correspond to a delete. And so we want to be careful here because we want to delete at the index path, not the new index path. So we want to check if let index path equals index path and remember, not the new one, but the index path table view that the elite rose at the collection of the index path. And again, you can apply an animation here. I'm gonna go and use a fade like that and that will remove the rose. And now, if you try to comment out the update in the move because we're actually not going to use this cases in our example, then we're gonna be told that our switch is gonna need to be exhaustive and it's gonna make us add this case is back. So what I'm gonna do here since we're not really using them, I'm just gonna do print, move Dutch results change type called and I'll do the same thing for the update. Just so it has something to print, but we're not gonna actually do anything there. Update fetched results changed type called Gordon built, and now our switch is exhaustive for all the cases. Now let's talk about these three methods in the complete context. So if we were going to insert a new folder, then controller will change, content will be called. That's gonna give us the opportunity to begin updates because we're inserting Rose that have animations. This table view begin updates methods, as the documentation says begins a series of method calls that insert, delete or select rose. So we'll use this right before we're about to do in Insert Rose at on our table view. Likewise. Then this methods gonna be called. That did change an object, in which case we either insert or delete. And finally, once that's executed, the controller did. Change content is called, giving us an opportunity to call end updates, meaning our table view is updated. The animations have completed, and we're all done with that sequence of events. Now in the next lesson will go ahead and plug in our table view delegate in data source and start hooking in all of the pieces to fetch and display the list of folders back in our table view. 14. 13 displaying folders: this video is gonna be all about displaying the folders that we saved where cash in core data back into our table view. And before we actually start coding any of our table view delegate in data source methods, let's head over into the main storyboard and begin setting up our user interface for our table of you sell. Since we haven't done much work for our cell yet, your cell should look like this, just the default cell in the table view. And so what we'll do here is we'll go over to our folder view controller and start expanding from the view hierarchy here. And if you were menu is hidden. There's a little option here to collapse or on collapse it. And with the folder view controller selected, we're gonna go into the view into the table view and select the table view cell, and we'll go over to the right bar here or the inspectors and open the right side. And we need to do a couple of things here in particular. We want to subclass this table view cell just cause we're gonna add some customization to it. And to do that what weaken dio is create a new class that derives from you. I table view cell. So what I'm gonna do just to keep the project organized is go into the root folder, create a new group what is called this table of your cells, and we'll go ahead and add a new table view cell to this folder. What we can do here is just a cocoa touch class and will make sure that it is folder table , view cell. Make sure that it is a subclass of you I table view cell and then go ahead and create the cell. Now, with the cell created, let's open up the assistant editor and what we can do here is just have the storyboard on one side and are folder, table view cell on the other side. And we'll just bring up the storyboard. And with the cell selected, we're gonna subclass folder table view cell for the custom class of the self. So folder table yourself like that and we're gonna change just a couple of things on here. Let's scroll over here and I'm going to increase the row height from the default value to 90 just to give a little more space. I'm also gonna change the background color of the content of you of the cell to something a little bit different. Just add some flair, and the next thing I'm going to do is add three. You I labels onto the cell so we can do here is going to the object library, look for label and just drag one over. Make sure it's in the content view of your cell. And what you can do here is we want three of these, so you can hold down command, see to copy command copy and then command V to Paste. And we'll just go ahead and position these labels. So I'm just gonna drag him over, flush the edges? I think so. And same here, flush to the edges for the bottom one. And with all three of them selected, what I'm going to dio just to make it simple is go down to the resolve of the layout issues . I'm just gonna reset to suggesting constraints for the time being to make the orderly that simple. I'm just going to select each cell. You can hold down the shift key, and all I'm going to do is just add a leading constraint, a top constraint, trailing constraints on the bottom constraint for all three of our labels at the same time . And I will just get a position them just like that within the cell. And now let's just go ahead. And I'm just gonna change the font for the top cell to something different. I'm gonna dio different fonts here of Erica Bold for the other labels. I'm just gonna change that thought from the default to Helvetica light sewing just to lights for that one and same thing for this one. And we shouldn't have any auto layoff warnings if we did that correctly. And so what these labels are going to represent is the folder title, the data that is created and how many notes are actually in the folder. I'm actually gonna screw things up just a little bit, just a little bit centered more evenly. And if you want to move years around, just remove it. He's update the constraint, Constance. And so with that out of the way we were going to do is create some I be outlets to folder table view. So to do that, just hold down the control, key, click and drag. And so the 1st 1 is going to be folder title. The second label is going to be folder date, and the third label is going to be note Count. Now, if you prefer, you can leave the label text in your label or remove. It doesn't really make a difference one way or the other. I'm gonna leave mine for now. And so with this out of the way, let's head back over into folder view controller and start coating up the table of you delegate in data source methods. The first bit of code we're going to write is a new function called Configure Table that's gonna look this funk configure people and all that's going to do is just table view. Got delegates equal self table view that data source equal self. All we're doing is telling the folder view controller to conform to the data source and her the you I table view data source a new high table. If you delegate protocols because it is going to bend the data and handle the touch events for when we make a row selection or delete Rose, which will implement in later videos. So let's scroll down in the bottom here. And ah replied, at the very end below are fetched results controller delegate You I table view the source. Until I get conformance, it will create an extension on older view controller that conforms to the U I table view delicate. And you are table view data source particles Now the required methods. We have to implement our number of rows in section and sell for row at index path. And for right now, just so we don't get compilation errors I was gonna return, uh, empty cell and zero and to get started with our implementation, let's start with number of rows and section first and then self road index path second. Now that we're using the fetched results controller, how do we tell our table view how many rows it should display And it turns out that he fetched results Controller has a fetched objects property, and from that we can determine the count of how many fetched objects are available after performing a fetch. And what that looks like is this. Instead of returning zero, we can return folder fetched results controller dot fetched objects don't count and because this is optional and maybe it's no If we didn't initialize something correctly, we're gonna wanna default to returning zero in the event that something here blows up. We don't want to force unwrapped this rather, just if it's no return zero and so that will give us back all of the objects available at the time. This is fetched because we're only dealing with one section and not multiple sections. This is simple enough to work for our use case, but if you need multiple section support folder fetch results Controller has a dot sections property as well, so you can figure out what section you can get the fetched objects from the section and handle this more complex table view cases. Now, for the last piece, we need to dio, we need to go ahead and actually return the cell and make sure the cell is displaying the title the creation date and the count of the notes available. And actually, one thing we want to make sure we dio is we set the cell, identify air in the storyboard, and what we can do here because I didn't do this previously is go back to your main storyboard. Select your cell. Go over to the attribute, Inspector, select your cell. Go over to the afternoon, inspector, and make sure sell or whatever your identifier is filled in in this field. And then let's go back over to full interview controller and start implementing our logic. So instead of that return we're gonna do is this gonna get a folder? Cell equals table, do you de que so at index path as folder table of you sell. After that, we need to get a reference to the folder managed object from the Fetch two results controller. And to do that, we can do this. Let folder equals fetched results, Controller folder, federal results controller dot object at index path. And so we could just pass in the index path and get back a folder immediately. And at this point, what we can do here is start setting up some of the user interface. Now, we could extend this further and rather than using a folder directly, maybe in Stan shaded View model, or maybe pass in the string elements to the table of you sell. But for the sake of simplicity, I'm just gonna handle this himself. road index path to start will do the title first, so Folder Cell got title her folder title. That text is going to be equal to the A folder, That title. Now let's handle the creation date. So to do that, we're going to start with and if let create, a date equals the folder that created Date. Recast that as a date because it's normally and estates or abridging it over to a regular date for swift compatibility, and we need to go ahead and actually form at this date. So to do that, we're going to create a new instance of a date for matter. And we're gonna set the string date format to a format we want to use to render our date. So I'm gonna pick something simple to character. Month two. Character day for Character Year. And finally, it will do the folder cell, that date folder Christian Date that text be assigned to the value of we're gonna do a string format here, so created that date for matter dot string from date created date, and so that takes care of actually displaying the date and the cell with the format that we want And now let's go ahead and update the folder. Note count for the note count. We can simply do your folder cell note count. That text equals the string format notes when you do Folder notes, which is the set of notes that belong to the folder managed object, and it's optional, so we want to use the count of it if it's available. But if it's nil or just has never been created or assigned, we will default to zero like this. Now we have all of the attributes displayed we wanted to hear is return the folder cell instead of the generic you I table do you sell and because this is an optional, because I had to try to cast it as a optional an optional cast right here to get a folder table of your cell, we have to return a cell here no matter what, not an optional. So we have a choice to force and wrap it like this, or if you want to do a guard letter and if let check and if it's nil of return, a generic your table view. So like I was doing before, that's totally valid. But in my situation. It's never gonna be No, it's always gonna be this. So for the sake of simplicity, I'm forcing wrapping this year. But you can handle this as you deem fit. And lastly, scroll all the way back up to the configure table method and make sure that we call that in view. Did load before we actually love the folder. So configure Table needs to be the first thing that fires, then load folders happens after that. Now let's go ahead and run this in our simulator. We're about to actually finally see the fruits of our labors here and actually cashing some data. So go ahead and hit. Plus, Icon was called This a test folder hit Save, and you should see if everything went correctly that your folder is automatically being created and displayed. You can see the name, the date and the notes on that folder, and let's just create another one, another test folder. Save that. And so here's the Here's where we can really put everything to the test, so we want to just swipe, kill the app, and you can double tap the home button to go up and completely kill it. from the simulator and to prove that our data has actually been cashed in core data. If we tap the core data, notes app, it's automatically coming back. So it's being persisted. It's not just happening in memory because we are fetching our actual notes from our sequel , light database, where they're being persistent. And so now we actually have some visual verification that everything is starting to come together and the next set of videos we're gonna actually start adding notes to folders creating the interface to get from a folder to all of the notes available, the leading entire folders to leaving notes and going through that whole process. 15. 14 deleting folders: in this video, we are going to learn how to delete folders. And in the last video we created some folders, and we prove that we can cash them and save them. But we want to be able to delete them, preferably when a user would swipe to the left on a cell normally and naps. You would see it leave button appear and you could delete a row. And that's exactly what we're going to implement. And so what we'll do to get started is go back to the folder view controller and we need to create a new function similarly to how we have a load folders. We're going to write a new function called Elite Folder and it's gonna look like this So funk till eat folder and inside of this function, we're gonna use the elite method from our persistence coordinator. So we're going to persistence coordinator thought to leave, and for the collection of objects, we're just gonna pass in the folder and then implement the completion handler. Just call this delete error to leave exception, and we'll just do a check. If let delete exception, we can just print out something to the console failed to delete Holder. But there and that will be enough for our delete function without delete function implemented. We simply need to call it at the appropriate place. And so we'll go ahead and implement the swipe to delete functionality that I was mentioning earlier. So we can do is scroll down to where our table view delegate methods are, and we'll do here is implement the swipe to delete in this area here, and we're gonna need to functions. To do this, we're going to need the table view. Delegate method can edit Row index path, and that looks like this. So to start typing can edit row index path and will return true. And we need one more function, and that's going to be table view. Commit editing style for road index path so you can start typing and commit editing style if we can pull it up that way. And yet this is a very long method name and what we want to do here because we want to check that the editing style is going to be the delete, and if it is the delete, then we can go ahead and delete the object at the index path like this and that code looks like this. If editing style equals sleep, we'll try to get our folder. So let folder equals thief older. That's results controller that object at index path. And we'll do, since that's an optional here's will wrap that up in another. It flipped check here the Kama and all we need to do at this point is we have thief older we wanted to lead. We can just hand it off to our delete function and let it run the actual delete. So to leave folder, and that will take care of that. Now let's go ahead and run this code and see what the delete delete implementation looks like. So here are the folders I created from earlier the test folder and another test folder. And now if I click and drag over, you can see that they have the red delete button by implementing those methods. And if I want to eat the test folder, I can go ahead and just have to eat. And there it goes. Now it's gone. Now, if you want to kill your simulator and restart it, we can verify that we actually deleted it from the cash. And you can see here that when I load back up for brand new time Onley, another test folder remains going to eat that eggs keep the same logic. Restart the simulator and you'll see now that we have no more folders because they've all been completely deleted. And so that is how simple it is to implement a delete on a folder level. And in the next videos, we're going to start actually implementing the logic to create notes and edit notes in link notes two folders. 16. 15 building the notes user interface: now that our folders are created. Belinda Leasable We want to be able to handle the on tap of a folder to go in and create view and delete the notes that correspond to a particular folder. And so what will start off doing first is creating the user interface for our notes view controller as well as the detail of you controller. And then we'll start to wire up all the rest of the logic. Let's kick this off by creating our view controllers first. So we'll go into the View Controllers folder and we'll go ahead and create two new view controllers. We'll do a new file. I do cook a touch class, and we'll call this a subclass of you Have you Controller and Swift, and we'll call it Nudes View controller than created. We'll also go ahead and create one more of you controller, and we'll call this note detail view controller and go ahead and create it. Now that our source files are created, let's go to the main story board and create the view controllers there as well. So in our storyboard, we're gonna go ahead and create to view controllers and we'll start with notes view controller first So to a view controller and just dragged that onto canvas. And what we need to make sure we do here is set up our sub classing. Um And so before we do anything else, go to the right here, go under the class inspector for the identity inspector, and we'll make sure we do notes view controller for our subclass. We can also copy that in for the storyboard identifier as well. And let's go ahead and create the interface elements for it. Now, um, this is just gonna be a table view for this view. Controller causes going to list all of the notes. And as you tap on a note, that will take you to the detail of the notes. So that's why we have to view controllers for this. So we'll look for a table view and dragged that onto our notes view controller and we could make it flush to the leading trailing top and bottom edges here, and we can just go in and set up some basic constraints. So you go down here in the bottom to a leading 20 a top 20 trailing 20 in the bottom 20 at the constraints. And now we need to go ahead and add a table of you. Sell for a custom self so you can search for just table view or table of you sell. Drag a cell onto the table view, and we need to make sure that we give this cell an identifier. And so we'll do Is with the table view cell selected. We'll go over to the attribute, inspector and where you see this identify reuse, identifier when we'll go ahead and give that ah, unique name, I'm just gonna go ahead and call mine cell and that will go ahead and take care of the interface elements for our notes view controller. Now let's also create the interface builder outlet for our table view. So what we can do here is go into the split screen view in the assistant editor, and what we want is our notes view controller, and we can just go ahead and delete the commented good that was automatically added for us , and we can select the table of you, hold down the control key, click and drag over to create connection, and if you subclass everything right. This should pop up and we'll just go and call this table of you. And so now we have our connection there. Now let's go back into the standard editor and we're gonna work on the note detail view controller next. So go back into our object library here, and we're gonna grab another view controller and drag it onto the canvas, and we'll go ahead and subclass this view controller So we'll pop open the right hand side here and we'll switch over to the class inspector and we'll do note, detail, view controller as our subclass. And we'll also make sure the storyboard i d. Is using the same name. And that's all set there. Now there are gonna be a few elements to the note detail view controller. It's gonna have a title. It's going to have the detail, text, a back button and a save changes button to capture any updates we may have made to that note itself. And so what? We'll start off doing a So I'm just gonna go ahead and give my view a different background color, and I'm also going to give it a navigation bar. Basically, add one So we go to the object library here and look for a navigation bar and just drag that over right onto the top. Here, I'll go ahead and give it some constraints. I'm just gonna give it a top constraint of zero. A leading constraint of zero in a trailing constraint of zero. Just add those dependent and we'll do Here is change the title of our navigation bar. And so what you can do is if you can select it, you should be able to access the navigation item, and you can give it a different title. So I'm gonna call my note being called whatever you want if you want a more appropriate name. And I also want to add a button to the top left here basically a back button to get out of this view if we don't want to see it. And so what I can do here is like for a button. We're gonna look for a bar button item and drag that right onto the actual navigation bar, and we can go ahead and have it selected in the hierarchy. And you can also see here if you're having some trouble selecting through elements. There's a tree hierarchy and you kind of cycle between your navigation bar, bar button items, anything in here if you having issues. I'm getting hold of things and instead of item for the title gonna change this to back, I'm going to change the tent just to black give you consistent with the rest of the color theme. Next, we need to go ahead and add the save changes button and we'll go back into the object library. Look for you. I button and dragged that onto the view controller. We really want this positions, can it towards the bottom center. And we can do here is with the but selected we can go back and update the title. You can change that text to save changes and I'm going to change the color two black And I'm also gonna change the fund. What's got into a custom? I think that should be okay. Gonna make it bold and make sure that's text is updated and reposition that. And so I'm gonna add a couple of constraints here. The first thing I'm gonna do is go down to the align button and I'm gonna align horizontally and container and I'm also going to do a bottom constraint zero, and that will take care of that constraint there. Now. Next, we need to go ahead and add in the text, field and text view and what kind of sandwich those between the navigation bar and the save changes button. So look for text field and drag bed onto the view controller. You can I get that? Maybe about a little bit below the navigation bar and just drag to the margin guidelines on the leading and trailing sides. We'll also go ahead and grab a text view. Well, Jack that onto the view controller rate below the text field, and you can kind of just use the margin guidelines here to help lay it out. And we'll bring this down to where the guidelines are for the button. Bring the trailing to the guidelines and the leading to the guidelines and, uh, bring our text field up. Maybe just a little bit more should be OK. They're, and we'll do here is select our text field, and we're gonna go ahead and set some placeholder text and center the text. So once you're in the interview, inspector for the text field. You'll have an alignment for the text. You can set that to center and you can set some, uh, placeholder text. I'm just gonna do no title hit. Enter and you'll see that we have some placeholder text show up here. And now for the text view itself is going to lead the, um, default text that they put in there. It entered. Clear that out. And now we need to go and set up some constraints for the text field indexed view, starting with the text field. First, make sure you have it selected. Go down to the align constraints button on the bottom here, and what I'm gonna do is just add a top. A leading trailing in the bottom will be roughly around 20 and others were constraints and also the text of you. So have the text of the selected that the line button again and we'll do a top leading trailing and bottom. And if all goes well, we should have no more constraint conflicts, and that should take care of that. Now let's go into our assistant editor here and hook up the Ivy outlets and actions for the note. Detailed view controller, So I'm just gonna pop into the assistant editor. Make sure you have note, detail, view controller on one of the sides here, and we'll just go ahead and sleet that commented, uh, navigation, that X good automatically adds, and we'll start with the back button. So hold down the after you select the back button, hold down the control key, click and drag over and make sure that it's an action connection here. Just call this back. We'll do another action for save changes. So selector save changes. Button, hold the control key, click and drag. Make sure you have an action and we'll call this Save and Connect, and now we'll create the outlets for our text fields. So starting with the title will select the title text, field Control, click and drag title, and for the detective, you select it. Hold down the control key, click and drag. We'll just call this detail text. Entitled might be a reserved word if I can't use that. Well, Dio it's called this title text, and so I just go ahead and dio select the text field again for the title. This I'm right click on it. Does he have a referencing outlet here. Go ahead and hit the little exporting to remove it. And what we can do here is just to leave that and recreate it. We'll just call this title text, and that shouldn't cause any problems with reserved words in the next part. What we're gonna do is now that we have our interface set up when we actually tapping a folder that's actually going to take us to the notes of you controller, and it's gonna have a similar interface where we can create a note and then tapping on a particular note will take us to the note detail screen. 17. 16 displaying notes: In the last video, we set up our user interface for our notes and note detail view controllers. The primary goal in this video is going to be to actually get to our notes view controller from our folders, you controller. And that's gonna work when we tap on. Any folder will go ahead and load all the notes under that particular folder. And to do that, we're gonna need to write some basically set up code to make that happen. And we'll go over to the notes view controller and get started here, and we're gonna need to create a function that will let us basically construct an instance of this view controller with the dependencies that it needs And so we can do here to get started is created new static function called aesthetic funk construct notes view controller. Don't give it some parameters here. So with managed object, I d. And you're probably not gonna be able to find this. So what you have to do is good at the top of your view controller and to import core data, and we'll go back here in this managed object i d and complete that we're gonna also inject the persistence coordinator. So persistence, coordinator. And finally, we're also gonna inject to the storyboard so we can do it from storyboard of type you I storyboard. And so once we have that set up here, let's go ahead and form at this a little bit. And the next thing we're gonna do here is to find the return type of this function, which is it's view controller. And, um, it's gonna be optional here and now inside of the function we want to do is, um, let notes view controller equal storyboard Dunstan, shave you controller with identifier. We'll do. Here is a string describing the name of the class So that's gonna be newts view controller that self. That way, we don't have to hard code it weaken. Just let the name of the class B the literal string for the identifier here and lastly, as no view controller and more return that the function here you couldn't build And now let's go ahead and talk about some of the parameters that we're gonna need to store now for the parameters we need to store. We want a store for one, the persistence coordinator so we can do a private bar persistence coordinator. We also want to store the folders managed object I d. Because we're gonna use that I d toe locate the folder. So we'll keep another variable private far folder managed object idea, and it's gonna have type N s managed object i d hallmark that is optional. And finally, the actual folder itself will keep just a reference of that so we can use that in our table view. Delegate methods So private far holder of type holder. You know, Mark that is optional. And inside of our constructing its view controller, Once we've been Stan Shih ated, an instance of it will go ahead and assign these properties. So notes view controller, That persistence coordinator is the Kourtney re passed in the managed object idea of the folder manage object I d will assign to the managed object of the parameter passed into the function. And so once we've done that, we have this set up here. Now we'll do next is create another function called load notes. Oh, do that right after beauty load and we'll go ahead and call it in view did load. And in this function, we're gonna actually try to locate and pull our notes back from our core data cache. Now, I look notes. Boulder's gonna look something like this. So first thing we're gonna do is start doing some if let checks. So if let persistence coordinator equals the persistence coordinator and let the folder Manish object, I d equals the folder managed object idea, the optional one. But then a second line here. And finally, if the folder can be pulled back from the coordinator like this. So persistence coordinator dot fetching context. The registered object for a particular idea. And that idea is going to be the folder managed object idea that we just unwrapped as a folder. If we can get all of these, three things will go ahead and keep a reference Self Doubt folder. You know, fix that type of here holder. Self dot folder equals folder that we just pulled back, and we just did that spelling. We don't use it there. It's basically what we're doing here is we're We have a registered object because we that folder already exists, and we can pull back that object using the I. D. And this is safer than passing the managed object directly into another view controller. What happens a lot of times when people don't use the object idea to locate the managed object is they'll go ahead and pass objects through various threads. And that's where you run into threading problems later on, because managed objects are really not threads safe, and you want to use the I D to find a particular manage object from the context it was registered in. And so that's the key. So in this case, the fetching context we're going to look for the registered object using the I. D. And in this case, we're expecting a folder, and that's how we're pulling it back from the coordinator directly. Now that we have these functions written, let's backtrack a little bit. Teoh are folder view controller, and what we want to do is handle the top of a folder to present or display our notes view controller here to connect the two. And so if we go back to the folder view controller dot swift, we want to scroll down to the U I table view delegate and data source methods, and we want to implement did select Row at index Path and we want to do here is when a user selects a folder, will figure out what folder they selected and basically. And Stan, she ate a new instance of the notes view controller, pass it the folder and then present it with all of the dependencies that it needs. And so to do that, what we can do here is we can stuff about this method, and we can go back up to our inside of our actual view controller itself and create a new function. So called this one funk display notes for older and the folder is the parameter that gets passed in and the way will implement. This is like this. If let storyboard equal self that's story bored and let notes view controller equals notes you controller don't construct. And that's that method we created earlier. That static function constructed notes view controller with the folder dot object I d. The persistence coordinator that is contained on the folder view controller and the actual storyboard. So we want to try to do all of this first, and we're just going to put this on another line and close to you brackets, and then we'll do navigation controller. Don't push you controller. No, it's few controller animated. True. And then what will need to do here is go back into our did select road index path here. And if let folder equals the folder fetched results controller, the object at index path, an index bath, is coming from the did select row at index path parameter. So if we could get a hold of the folder, then we'll call display notes and pass in the folder. And so that connects us to actually getting to our view controller. So you should have now is when you tap on a folder, you'll be right into the notes. Now we need to create the interface for loading our notes and actually creating notes in the detail screen. So let's wire that up now. So going back into the notes view controller, we need to create first a few more methods here, and we'll start off with an easy one. So we want to make sure that our table view is actually configured right now. It's not so. What we can do is hook up a new method called funk and figure interface, and inside it here we could create a new method called funk configure table. And all this is going to do is assigned the table views delegating data source. So, table of you, that data source will be assigned itself table view. That delegate will also be assigned itself and will make sure that we call this function in . You did loads so called configure table. Damn it. Fix this tape and we'll do here. Scroll down and creates a extension on our notes. Do controller. That conforms to the table view delegating data source particles like that. And so now we need to start looking at how we are going to hook up the actual methods in here so we could do to start off with number of rows and section. And this is where the folder comes into play. So a number of rows in section and what we're gonna do here is we're going to return the folder that notes that count. And if that happens to be nil, for whatever reason, we'll just default zero. Now, if you remember from earlier when we construct the notes view controller we provided with the object idea of the folder and then when we load the notes were using the object i d to load the folder in a sign it. So that's how we have a reference to it down here now, the next method we need to implement a cell for Rohit Index path. And so that's gonna look like this. De que reusable cell with identifier for the identifier cell that we've signed in the storyboard earlier and with the index path. And then what we want to do is actually display for the cell text, the note title and information for the self. And so we'll do that like this. If let notes equals folder dot notes and that's the set of all notes. What we can do is call all objects to get back a collection. We can cast that as a collection of notes, and once we have a reference for the notes, we can assign the cell text to the title like that, so self detail or don't text label text will be assigned to notes at the index path dot rope for this elected note title, and we also have the full note. But that's going to be reserved for the detail screen. The title will be good enough to display for the cell itself. And lastly, we'll just return the cell from the method. Now, our notes view controller doesn't really do much if it can't display notes. So we need to make sure that we can actually create new notes from it. And to do that, we need to set up our no detail view controller first. So we're gonna switch into that and add a couple new properties. Now from earlier we just set up the user interface from before, but now we're gonna actually store a couple properties that are needed here. So to start off, I'm gonna import your data as the dependency that we're gonna need in a minute. And we're going to keep a reference to the persistence coordinator. So private bar for assistance coordinator. It was Mark that as and optional persistence Gordon here we're also going to keep a reference to the parent folder of notes, and that's gonna like this private, far parents holder. I think we'll also mark that as optional here, similar to what we did before. We're just gonna create a little static factory method here, too. Instead, create a new instance of the note. Detail view controller So static funk Create note, detail, view controller for the persistence Coordinator, the Parent folder folder and the storyboard to load the view controller from and we'll return back and optional note detail of you controller like this, and that's gonna look like, let's note futile. Have you controller equal storyboard, substantiated view controller with identifier for the identifier. It's gonna be that fancy way of getting the class name from a strength So string, describing no detail view controller that self as a note detail view controller, we'll return this from the function and to sign the parameters accordingly. So note, do you tell view controller dot persistence coordinator. It's a science of the one passed in and note detail view controller Parent folder will be assigned to the parent holder Just a quick built here and verify I'm gonna actually return this as optional kids that can't be found. And next we want to go back to the notes view controller and call this function. So back in notes Do controller, we're gonna create a new function called create note. It's gonna look like this so we're gonna if let's storyboard equal self dot story word and let note detail view Controller equals the note detail view controller dot Create detailed view controller. The factory method we just created a moment ago passing in the persistence coordinator, do you folder and the story board it was Put the another check on the second line. So if we can do that, we'll do here is present no detailed view controller. And it made it true. Completion No, just build that real quick and verify everything's building. And so far, so good. So lastly, we want to call this when we tap on a button to create a note and we need to go ahead and create that function real quick. So we're gonna go ahead and create another function in here called Funk Figure Interface, and this function is simply going to create a bar. But an item that has a programmatic button that button's gonna call, create note and take us right to the details screen. And so how we're gonna implemented is like this navigation item dot right bar, but an item to be assigned to a, um, party tonight. Um and I'm gonna do dot Add So it's a little plus icon the target's gonna be self or the notes you controller and the selector is gonna be create notes that's going like this and that's going to force us to expose create notes. Objective seat. Um and so you're gonna have a at O b J c for the create note here. And finally, we need to make sure we call, actually, one more step here. Um navigation, Adam. Right bar, But an item a tent color equals, not black, just to keep it consistent with the rest of the app. A theme here and we need to call our configure interface method in view. Did load of our notes view controller. And so there we have it. So now let's go ahead and run this in our simulator and verify that we can get to the details screen successfully. So here's our folder and here's our notes. Here's the little plus icon we created programmatically and clicking on It creates the new instance and presents the note Detailed view controller. Now, in the next videos, we're gonna complete actually saving new notes, updating existing notes, as well as to leading notes from folders 18. 17 creating new notes: this video is a continuation of where we left off from before, and by the end of this video we're going to actually be able to create and save new notes and then see those notes show up under folders and then we can go into them in view them. And to get started, we need to go back to the note detailed view controller and flush out a few more properties and functions that will help us achieve this so we can create a new function in our no detailed view controller. And we'll call this function save note. And it looked like this funk save note with title in text. And I am allowing these to be optional in the event that empty text have saved or nil or whatever, they're not strictly required. And in this function we need to check that we can get the parent folder and, more importantly, the parent folders managed object context because in the parent folders, context is where we're going to create this note. So that logic is gonna look like this if let Parent folder equals Parent folder and but Parent folder context equals parent folder dot managed objects context. Now, if you recall, are managed object hierarchy. It's a parent child relationship. And so it's always safer to try to get the same context that the parent object was created in in this case, are folder and use that same context for creating our child object in this case, the notes. That way we reduce the possibility of running into threading issues where managed objects are being accessed from contexts that might belong on a different threat or were created in a different threat. This approach at least allows us to enforce that her notes are going to be created in the same managed object context as the folder was created in. So it's a safer approach, which is why we're doing this check here. Now let's proceed to actually create the note so we'll start off by creating a new note. So let note people's note from and created in the Parents Folder context note that title equals title of the parameter passed in. No doubt, full note equals the text string parameter passed in. No, that created Deep is gonna look like this. We're gonna use the current date and have cast that is an estate And now one other thing we're gonna do here is the parent folder has the ad two notes method one of those convenience methods where we can just add a note right to a folder. So what we're gonna do here is these ads notes and pass in the note managed object we just created like that. And so at this point, all we want to do is save the changes. So that way it persists. And in this case, for us that it persists to our sequel light database cash. So we'll do here is persistence coordinator that save changes in parent folder context and will implement the handler a weekly reference self. And we'll do here is inside of our dispatch method here and actually closed this off here inside of our handler. Gonna do dispatch cute that main that a sink to get the man thread because we want to make sure that no matter where we save changes from in the future, whether it be a background, context, view, context, we always want to get the main threat because we're gonna be updating some you I afterwards and at this plant, we're going to implement a new function and right below our save, we're gonna do funk, dismiss note detail, and we're gonna call that inside of our main threat dispatched. You sold yourself that dismiss no detail, and at this point, and it's created, we want to go back to our notes, view controller and view the changes we just made. Now let's implement the dismissed no detail method. We just added, and it's really simple all it's going to do it was dismissed the view controller like this , and that's all we need to do to get back to our notes table of you controller interview the changes. Now we're not quite done here because we need to make sure that we hook up the save logic for when we tap the save button as well as the dismiss logic for when we tap the back button and as well as configuring are you I text field delegate. So let's do that. First, we're gonna create another function above save called configure. So funk configure that's gonna look like this. No title that delegate equal, self and at the bottom here, we're gonna conform to the text field delegate, so scroll down at the bottom, creating new extension. A note detailed view controller like conforms to the U I text field delegate method. The method we're gonna implement is the text field should return, and we'll simply return true and we'll call text field The parameter passed in here that resigned first responder so our keyboard can actually be dismissed if we were typing in a new note title. Now let's go back and to review did load and make sure that we call this configure method we just created. And so we make sure our delegate is assigned on the text field. And now let's switch into the assistant editor and we're gonna hook up the Ivy actions for our save and dismiss buttons. And so what we can do here is starting with the save button, go into your no detail view controller and just hold down the control key. Click and drag, and we're gonna go ahead and creates a new action. We'll just call this safe changes and will also create a new Ivey action for this back button here. And so we'll do years control key, click and drag. We'll just call this back, and our back function is simply going to call dismiss. So we'll just call, dismiss no detail like that and are safe changes. Function is going to call, save note, and we're gonna pass in the text field and text of you text that whatever happens to be in there so we'll do it like this. Note Title that text and note text for the U I text view dot text for all the detail text. Now we're always ready to test everything out, but there's a couple more tweaks and adjustments we need to make. Let's go back to our notes of you. Control a real quick and what I want to do is an implement of you will appear and what we're gonna do in view will appear is reload Are notes data source. It will do table of you dot reload data, and that's gonna reload all of the changes made to our notes. So if we create a new note will see those changes load immediately upon returning from the details screen to the list screen. One thing we have to be aware of is Iowa's 13 changes. How, if you will appear, is called for certain larger plus size devices you'll appear is not called by default if the motile view controller presentation is not full screen. And so if we go back into our note details you controller, we actually don't specify that. We want the presentation of this view controller to be full screen. And so on. The larger devices on IOS 13 our view will appear wouldn't be called when coming back from this view controller or dismissing it. And so that would basically not call. View will appear in the notes list and not update our list. So to fix that is quite easy. What we can do is specify that we want this to be presented in full screen, so we'll do note detailed view controller that motile presentation style equals full screen . Now we're ready to test. So in the simulator, I'm gonna go into my pre created folder called Test Folder and you'll see that I have two of them in here, both of them with the notes count set zero and we'll tap into the test folder and hit the plus icon in a new note. We'll just create new note and for the detail text. New note saved in core data save changes. And sure enough, we see our new note actually being created in our notes list, although we haven't styled the cell, so it looks a little bit plane. And if we go back to the folders, you'll notice that our notes count here hasn't been updated. It should reflect the value of one, but it's currently zero. Not understand why. Our notes count is not updating. Let's go back to our folders view controller, and we want to do is go into the search results. Controller delegate did change object method and you'll notice here that we implemented everything except update, and now we're ready to implement it. And so what's happening is when we are creating a new note deeper down in the view hierarchy, this will actually get called once we've added that note to the folder because the folder is getting an update that hate something has changed, and I can see those changes so you can do something with it if you want Teoh. And so what we want to Dio is ideally reload the folder row so that it displays the correct count of notes that it now has, and so what we can do here is we're provided the index path from the delegate method. We know the change type, and we're already switching against that. We know a new index path if something was added, but we don't need that for the update case, so we can simply do this if let index path equals index path table of you that reload rose and it requires a collection of index paths. In our case, it's just one. So it's gonna look like this index path. And for a particular animation, I'm just gonna do automatic. And so now just remove that little print method. Let's run through our little scenario again. We're gonna create a new note, go immediately back to the folders and verify that the count is updated to reflect what we just created. So we go into the folder here and you see, already has one in there were created from earlier it, plus call it note to, and this is another note save changes. We see that it immediately shows up in our notes list. We go back to our folders. Now we see that our test folder has a count of to accurately reflecting the changes we made in the notes list. Now, if our notes view controller was using a Fetch two results controller like we were doing in our folders view controller, then we could get live updates for changes made to the notes such as updates, the leads, insertions, etcetera. And that would be convenient because then, in our view, will appear method in the notes list or the notes view controller we wouldn't have to call reload. Data on view will appear, but it's just easier because once we have a folder, the folder being the parent object that contains the notes, it's just easier to pass the folder to the notes view controller and then just update that sub collection rather than having to configure a fetched results controller and then have to do another fetch for the notes and have to write all that logic, we avoid having to do another fetch because we've already fetched. The folders and folders contain the notes so we can just pass those along. But to get the initial route or top level object. That's why we use the fetch results controller and the folders of you controller, as opposed to the more manual approach we did in the notes view controller of just passing the folder around. Now, in the next video, we're going to continue forward with implementing the updating of our existing notes and deleting existing notes as well as styling some of the user interface for our notes sell so it looks consistent with the rest of our core data, notes app. 19. 18 updating and deleting notes: updating notes is going to be handled in much the same way as creating new notes with some minor changes to support this and in the note detailed you controller. That's where we're going to get started. And we're gonna get started with a new property, and the new property is going to look like this Private bar Existing note. Take note and will market is optional. Next, we're going to modify our save note function to check for an existing note to do an update on or create a new note if we do not have an existing note. But before we write that others going to do one consolidation here I was going to actually do instead of the double checks here doing if let Parent Folder context equals the parent folder that manage objects context from before I was gonna consolidate that and what this is gonna look like and ultimately is this. So this is gonna be the out statement and the statement's gonna be if let existing note context equals existing note dot managed object context. Well, just format this. Now we have a note to update, and so the way we're gonna update it is like this existing note that equals the text from the text field. Existing note dot title equals the title string passed another function, and then we want to save the changes. So for assistance coordinator that save changes in context and in this context will be the existing note context will implement the completion handler. It's gonna look much the same way as creating our notes. It's gonna reduce some of the stuff we don't need here. Update error. Just case we need it. And when the save is complete for the update, gonna actually call the same code we did for creating a new note. So dispatch que main a sink, grab the main thread and then dismiss the note detail view controller and we'll give a week reference to self now because we haven't existing note. We want to make sure that any time we are updating a note, we are binding the notes title and full text to the text fields of the note detail screen. And we can capture that in the configure method that we wrote earlier. So all we need to dio is set the text field and the text of you text so note title dot text beside of existing note. Dot title and the note text that text because the existing note that full no text The next thing we need to do is modify the create no detailed view controller to add in a new parameter for the existing note. Now this is going to be optional, so it's gonna be existing note of tape note nor mark that is optional. And what we'll do here is when we in Stan, she ate the detail view controller. We will assign this property accordingly, so note detail of your controller dot existing that equals the existing note parameter. Now we need to go back into our notes view controller and make some updates because, as of right now, our code will not compile and really even work until we fix this. Now the main problem is we need toe. Determine what existing note or what? No, we want to update and pass that in when we are creating notes. But in reality, this isn't just creating notes anymore. The function that we wrote previously, it's gonna be creating or updating notes, and so we want to make a modification to be able to take a optional existing note parameter and here and then basically pass that forward to the note details from you controller, and that's gonna look like this. So we'll modify the name of the function to create or update note, and we'll have a parameter here. Existing note. The market is optional because we could be creating a note and we don't have one to pass in . And so at this point, we can do here is passed in the existing notes. So we'll go ahead and fix some of the formatting and compilation errors and here and we'll have Exco tell us what about the parameter. So insert existing note and this new parameter will be the existing note passed into our creates or update method. We also need to fix this selector name problem right here. And the way we're going to do that is we're actually gonna create a new function. So this is gonna be funk create new note kept. We're going to move the adobe J C tag to the create new no tapped method here, and we're gonna replace, create note with creating an attempt, and that will fix that problem now creating, you know, Tapped is actually going to call Creator, update existing notes, and that's gonna look like this creates or update existing note. And now, because in this case this is the old way of creating a new note. We don't have an existing notes, so we just passing now. Now we need to handle the actual selection of the note next. And that's going to come down to our did select road index path from the U I table view Delicate protocol. And what we'll do here is implement that so did select Throw at index death. And then we're gonna do a check if let notes equal folder that newts all objects to get the collection from the set. Cast it as a collection of notes like that. Then we'll call creates for update existing note, and we'll do notes at index path dot ro. And this is how we actually get whatever note is in our screen. So this will actually allow us to present the no detail view controller for tapping on a particular note and then updating it. So let's go in the simulator and test that out right now and see how it works now in our simulator. We'll start at the folders, go into the notes, and I have some pre existing notes from before. And so I'll start with note to I selected by tapping the row. And now you can see that the note to is showing up with the pre existing text and the pre existing title. And and so if we go ahead and change this, so let's say updated No. Two. And this is another note with updated text, and we save those changes. You'll see here that when we come back to the actual notes list, the updated title has already been captured. And so if we select the updated note again and go into it, you can see that the text is exactly where we left it before. However, to see that it's truly persisted, we can actually kill the simulator. And so now we re started, and this is a completely new instance of the app. We go back into our folder here and we see we still have updated note to just in a different order without was stretched, and this is another note with updated text along with the title. So it's Obviously, the update is being persisted after we save the changes. Next, let's turn our attention to the deleting of notes, and we're going to create a new function. And we'll just do this in the notes view controller and right below our create method, we can do a folk delete note at index path. We'll start by removing the note from the folder and that looks like this folder. Remove from notes, the value of notes and the notes gonna be the parameter we passed in, just like there is a method for adding there's a method for removing it, so we'll take advantage of that. Finally, we'll save the changes. So if let nipped context, it was note that managed object context we'll call our persistence coordinators save method . Inside of the note context, you will implemented the handler. This format has a little bit, and because we're deleting in saving changes, we always want to guarantee that we have the main threat. If we want to do updates to the user interface and we dio, we want to update our table view and show the row being removed. So we're going to get a reference to the main thread Dispatch que. That means that I think and don't do self the table of you. That elite arose at the collection of index paths. We want update in this case, just one. And we'll do any fade animation to keep consistent with how we were deleting are folders in the folders view controller. So this will look and feel the same way. Now that's implement the table view delegate methods needed to do a deletion and then we'll test out the changes in our simulator. So scroll back down into the nets of you controllers, table view, data source and delegate protocols, and we're gonna implement two methods in here table. You can edit row at index path and we'll return true, saying that we are allowed to make modifications here, followed with commit editing style. This is a long mam's. A table of you commit editing style for road index path, and what we need to do in here is make sure that the editing style is a deletion. So if editing style equals to leave and then and let notes equal folder that notes that all objects as the collection of notes now we'll just call our sleep function. So delete. Leave notes so we'll do the notes at the index path Dairo to get the selected notes it elite and will pass in the index path provided in the delegate method. So now our delete logic is all wired up. Let's go and test this out in the simulator. Let's go into my test folder that has two notes inside of it. And, um, let's go ahead and delete updated note to Well, just swipe over here. Hit, delete and see it. Now that it's gone, go back to the folders we see we only have one note. Go back into the notes. It's gone from there as well. Let's restart the simulator and verify that the change persists throughout life cycles. Okay, back in the simulator again, we see notes. Count is one. And sure enough, our updated note is gone, so they delete persistent the last portion of this video. We're just going to style our notes table of you sell to look and be consistent with what we did in the folder table view cell. So we'll add some labels there, make it a little bit larger, and then update the background color. So we'll head back to the notes view controller and get started there. And thats view controller. We want our self road index path to be using a custom cell and updating all of the labels to display the title and the created date for the notes cell for every one of them and will do to get started is go into the table of yourselves folder and we're going to create a new note table view cell. And so you can just subclass or create a couple touch class subclass you. I table you sell, and we'll just call it note table. You selling creative with the cell created, we're going to go back to the main story board and we're going to select the notes view controller and we're gonna go into the view hierarchy and look for the cell on the table view like this, and we're gonna select the cell, and then we're going to go over to the custom class in the identity inspector and we're gonna subclass note table of you sell, and I'm also going to increase the height to 90 from the default value. Just so it's a little bit larger and I'm gonna also change the content, views, background color to match the folder table view cell. And we means me recently is colors there. And I want to make sure that we add the labels to our no table yourself. So I'm gonna look for a label in the object library, drag it onto this cell. I was gonna bring it to the edges here. We came to command. See it a copy this label and create another label below it. So command, See, and in command. Veto pasted right below. And I was gonna bring this of a little bit and try toe, select both of them and just get them in the center as best I can. And lastly, I'm gonna add auto layout constraints to these. So I'm just gonna go down here to the add new constraints leading top trailing in bottom. I'm just gonna change the font on this top title label here. So it stands out. So I'm gonna go into the attribute inspector, go to fund. Good. A custom. I'm gonna do Helvetica new and bold. And lastly, we just need to create the I B outlets for these labels to the note table of you sell so we could go into the assistant editor. Bring up the note table of you, sell in one side and storyboard on the other, and we can select each label one at a time with the top one. Hold down the control key, click and drag to know table of your cell. We'll just call this title for no title field and for the 2nd 1 we just got this note. Sub title. Field connected. Now let's go back into notes view controller, and we're going to start working on Self Road Index path to make sure that we are using our no table view cell and then updating the labels for the note text. Now let's use our notes table of you sell here. So in the D. Q. Reusable cell we're going to add in as note table of your cell, and that's gonna make it optional here. So I'm gonna dio force on rapid here because I know it's not going to be no, in our in our case for the label here, we're just gonna treat. This is optional now, so we'll add a little question mark there, and instead of the text label. We're gonna use the note title field instead, and we'll keep that as it So that's gonna bind the no title to the title text field. And then we're gonna also use the subtitle field Next So we'll do here is trying to get the date. So we want to display the created date for this particular note. So if let note date equals notes at Index Path, that rope that created date as date like that will create a date for matter. Date for matter, he pulls anyone date for matter? That date format will be equal to month, month, day, day and the year like that. Finally, we'll do self that sub Her note. Subtitle field dot text equals created that I want to date for matter that string from note date like that. Now let's go ahead and of you are changes in the simulator in the simulator. Let's just play around a little bit. We'll go into the other folder that doesn't have any notes, test test, no to text and it's safe changes. And here is our note cell after we made this update, so it's looking pretty good. Now go back to the folders making delete this entire folder. If we wanted to, we could go in a test folder we can view updated No tu from before you can see here that the updates were there. So everything has come together nicely. And in the next video, what we're gonna do is explore some concepts for how to pre load cached data that could be retrieved from a remote server or even locally bundled in the app. So you can pre load your database with data if you need to for certain app, updates and whatnot. So we're going to discuss some strategies for how to achieve that next. 20. 19 preloading cached data: a common use cases for APS to have data that is preloaded or imported with the up when it's installed by the user. And this is useful for like, a starting state of data for certain things that you want the user to be able to see without having to download it from a remote server as soon as the user gets Europe. And in this video, we're gonna explore some techniques for pre loading cash to data. Our use case for this tutorial is imagine. We install the coordinated notes that for the first time, and we want to have some folders with some notes already pre loaded. And you can see Apple does this with some of their APS, and many APS tend to do it where you install it for the first time, and there's already some data there. How would we do this? And while there is no right way for how to do this, there are multiple ways of doing it. You can pre load data from a file that's bundled with the application. You could pre load data from a sequel light database that's also included bundled with your application, but we're going to do is pre load data from a Jason file that has bundled with the core data notes APP. And we're going to do that as soon as the APP starts up for this lesson. In particular, they're going to be two files that are included with the starting source code of the project, and they're going to be the data and border dot Swift and the preloaded folders dot Jason. And so let's take a few minutes to talk about each of these files. The data importer has been pre written, so all you need to do is call a couple of methods, and it will take care of everything for you. But let's take a basic look at the structure of this class so you can understand what it's doing and understand some of the techniques for pre loading, cached data. And there's only a couple of methods in here that we really need to know about. One is the initial Isar and the initial Isar. For this data importer takes the persistence coordinator that we worked with up to this point and the name of a file that we want to pre load data from in this case, the Jason file that I mentioned earlier now for the actual methods were going to be using. On the data importer, there's only two. There's going to be a import preloaded data method that has a completion handler, and what it does is it tries to open the Jason file that's bundled with the application bundle, and if it can open the file, it goes ahead, and it tries to parse the file and create managed objects of folders and notes from that file and then import them into a database in the import folders function here. And all this does an import folders is it takes a array of dictionaries of folders, and it gets a private context from the persistence coordinator, and it imports all of our folders and notes in a background context. So this is happening off of the main threat in a background thread, and once it completes, it calls safe changes to persist. All of the important that we just did. It marks a flag in user defaults, saying that we have successfully imported the data for a particular key, which is just a string value, and it calls the completion handler in a successful case. And so all of this kind of goes on in the background while your APP starts up. The other method in here that is of interest is has completed local data import it returns . Sure faults if you have successfully previously preloaded the data and that's useful to know. So we don't accidentally duplicate imports each time the application launches. And this flag is toggled true when we have a successful data import down here now, because I'm giving you these files and I've already written this code for you. You want to make sure that you incorporate them into the application target successfully. Otherwise you might have some issues. And what I mean by that is when you actually add the data imported to the project, you want to make sure that when you include it the application or the files target membership is coordinated notes, app, and that should be checked off. If you select the file and then you go over here to this little tab here, you'll want to make sure that the coordinated no tap has a check next to it. Likewise, if you select the preloaded folders dot Jason, you want to make sure that the target membership is coordinated notes up. What that means is that this file is going to be included locally in her application. Bundle, along with other Resource Is, and this file has to live in the APP in order for it to be found and then loaded into memory. And if it's incorrectly referenced than none of this will work now let's scroll on down to the bottom of AB Delegate and you'll see are configured persistence coordinator from the earlier videos we're going to do is below this function. We're gonna create a new function, and that's gonna be called funk pre load cash data. Using persistence coordinator, we'll pass it in as a parameter, and what this function is going to do is it's gonna talk to our data and border and say data and border the import preloaded data. It will implement the handler, and so this handler has an error if there is an exception thrown during the import, so what we can do is being format. This is important error, and we could do a check if let's imports error people's important error. But prints out the air failed to import cash data belts. Then we've succeeded, and we'll just print successfully imported the preloaded data like that. Now he needs actually call our pre load method. And what we can do is go back up one method to the configure persistence coordinator from above. And what I'll do here is actually configure and then call that method. So we want to configure the data importer like this data importer equals data importer, and you'll pass in the persistence coordinators parameter and the name of the file. Now, for the name of the file, you just wanted to match the file preloaded folders like that. So it's gonna look like this for the string preloaded boulders and make sure you type that right. Otherwise, this file is not gonna be found or loaded. And once we have specified our initialization, we're going to go down into the construct core data stack method here. Now, this has to happen first, because this is where everything is configured into memory and initialized. And if we try to import before our stack is set up, then it's not going to work. And so what we're gonna do is going to do in else and we're gonna we're gonna actually handle this. In the case of the success so else if let And what I want to do is I'm gonna create a week reference to self because I'm gonna be using self here, so weak itself inside the closure we're gonna do else if let's strong, self equals weak self will do strong self that freeload cached data using strong self that persistence coordinator. Now, before he tested out in the simulator, you have to remember that this import pre load data is happening on a private context in the background. So it's asynchronous. And if we go into the import pre load data method here we go into import folders inside of the data importer. You remember that we're using a private context to do the import in a background threat. Now, why is this relevant? If you go back into the folder view controller and you go into the load folders method we wrote from a long time ago, you'll remember that we are using to be a fetching context or like the main thread context in load folders. And so what could happen is that load folders executes first, then the importing of the cached data finishes sometime later on, and what will happen is when load voters will called. No folders will be fetched because this fetching context is unaware of the changes that were made in the private context when we were importing the data. But the question is, how do we make sure that this fetching context is aware of changes that are happening and knows how to respond? An update to those changes from the other context. Luckily for us, core Data has a notification that we can subscribe to for any time a managed object context actually saves. And in this notification, we know what manage object context was saved, as well as keys that were changed for the updates or the deletions and insertions. More importantly, that allows us to actually call another method called emerge changes from context. It's safe. And if we take a look at the documentation on this one, what it does is it merges all the changes from a notification. So what we want to do is we want to subscribe for this notification in the folders do controller, and then if we get that notification, we want to call the thief etching context dot merge changes from the context. It's safe notification and then passed that in. And so that basically informs our fetching context that some changes were made in a private context and that they should be merged into the fetching context so that our table you could be updated dynamically. If and when this happens, this could present itself. If you're importing much larger data sets may be thousands of rows of data instead of the handful that I have in my Jason file. Now let's go back into the folder of you Controller and subscribe for this context. Change notification. We'll implement the notification handler first, so we're gonna do an Adobe J C. Folks. Context did save. It's going like this notification, the actual notification from the handler and what we're gonna do here is we're going to say persistence, coordinated, are fetching. Context, merged these changes from the notification and so all that looks like is this for assistance coordinator emerge cut fetching context, merge changes from context. It save passing the notification parameter here, and that's it. Now we just need to subscribe for the notification imputed load, and that looks like this notification center a default that observer itself selector context did save and now the actual name of the notification. And this is going to be notification that name dot N s managed object context did save. Make sure you get the dead because there is a well, a well, we want to make sure we catch it at the did safe and for the object I'm gonna pass until and so now we can do is we can go ahead and run everything in our simulator and verify that the pre loading of the cash data is working. Assuming you've referenced everything correctly, you should see two folders show up when you launch your app for the first time for the preloaded folders and you can see here at the bottom of my console successfully imported all of the pre loaded data. And now if we go into these folders here, you can see that we have some stuff scheduled. That appointment, just some default text. That was all coming from that Jason file bundled in the application and because it was transformed into manage objects when the import process all of that was saved to our cash. So a user could start off the app completely from scratch and have this data, and then they can add onto it so you could go in and make a change, like instead of return Amazon package. Maybe you return Best buy package, for example, could save changes to that note, he said. Shows appear you could maybe delete some other ones you wanted. Teoh. You go back to the folder level and you see that this changes show up here, and then they can create your own folder completely. So my folder, It's safe and you'll see that it plays along just fine with the other preloaded data because it's all stored in the same database cash. Now, if you've really been paying attention, you may notice that there is a bug in my pre load cash to data logic. So I've already run this import one time. Let's run the simulator again and see what happens. So so it looks like we're getting duplicate imports. Now we have, ah, two sets of the data, and if I run it again even worse now, there's three times as much preloaded imported data. And so what we need to do is take advantage of that other method I was talking about for determining if we've already pre loaded data before we attempt to do it. And so what we'll do here is we're gonna add another check right before we do our pre load cash data, and it's going to look like this. So if let strong, self equal, self and strong self the data importer has completed data import, and we want to make sure that it has not. So he wants you do the negation of that. So has completed local data import set defaults. Now I'm going to reset my simulator and will run through this couple of times. Okay, so here goes Thief first time through, and you can see here that we've got the first import. And now let's run it a second time and see what happens on Lee. One import happened, and because we marked this has having been previously imported already, this won't fire again. Hence we won't pre load multiple times Now, this is just one strategy for a pure offline cashing systems import data. It's very simple. It does not require a server, but it may not fit your needs and so there are obviously other adaptations to this which may involved talking to a server and getting response back and then determining if you need to import data or not to and as well as different formats, you might not use a Jason file. You might use the C S V file. You might have a sequel light database bundled with your application that you're gonna import. But no matter which way you go about it, the general process is roughly the same. You have local assets in your application bundle. You need to parse those. Turn those into manage objects, save them in your database and then update your manage object contexts wherever you are listening. And so in any view controller, if you need to get notifications for things that might not happen in the application startup flow or might take a non deterministic amount of time, then you can subscribe for notifications like this and get updates just to make sure that you are updating everything after you've preloaded data successfully