Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
  • Overview
  • Transcript

2.1 Set Up the Core Data Stack

The Core Data stack is at the heart of an application. It allows you to connect to a persistent store and handles reading and writing. In this lesson, I’ll show you how to set up Core Data in your application.

Related Links

2.1 Set Up the Core Data Stack

Hi and welcome back to Get Started with Core Data. In this lesson, we will be setting up our Core Data stack to start using it. This stack is the fundamental part of each Core Data application. To illustrate this better, I got a diagram by our very own Mark Jacobs who has written a series on Core Data on Tuts+. It illustrates how the pieces fit together. Let's start at the bottom. This is the persistent store. Meaning, you don't database, which is stored in a file or an in-memory one that doesn't get saved. Then we have the managed object model, which is like the schema or object ref. It creates the data model from MOND files, which are the model definitions in a binary format. The persistent store coordinator is the heart of the stack. It is responsible for talking to one or more stores to read and write data. Load the data model and expose everything to the last component. The managed object context. It is what we are using the most in a typical application. We can request data from the persistent store coordinator through the context and tell it to write models to the store. Now, that was the simple version of this thing. Since it isn't a very practical one, I will show you some other patents that are used more commonly in non-tiny applications. Robert Edwards from the Big Nerd Ranch wrote an article introducing their own Core Data stack they use for customer projects. Where he compared different stack design patterns, both the pros and cons. If you're curious, I encourage you to read it. One thing you should be aware of before I explain different patterns is that the contexts don't always have to be on the main thread. Which is normally the big limitation of the default stack. They also can have parent context. The first pattern is a shared coordinator that serves multiple contexts at once, which have no parent-child relation to each other. While it is good that the more work intensive tasks are handled in the background. Ensuring contexts are consistent to each other has to be done manually. Then we have the nested managed object context pattern, which is what we will be using for the most part, and I will explain it in a second. The Big Nerd Ranch stack takes the nesting principle but adds a second coordinator for doing batch work. Our stack of choice has a persistent context that is responsible for talking to the coordinator. As well as a main context that will be used by the application for fetching and saving data. It will propagate the changes up to the parent context and saving. Since we won't do work-intensive tasks, it won't make sense to add another layer of complexity. But you will learn all the principles and should be able to expand it to create worker processes yourself. So, let's get started on implementing it. To start with the same code I am using here, check out the initial commit of the course repository. First, I need to create a new file that is going to be the Core Data stack. And also, import the Core Data module. I'm going to create this iteratively, so you can also chose to not have a persistent context, and only use a main context for your application. So let's start at the main context. I'm going to create a lazy, initialized variable that will call a set up function if read. In the setup function, I need to initialize the variable that we will use later, and also create a set up lambda that does the initialization. I have to do this this way, since we need to color the two different points in this function. The context itself is initialized with a concurrency type. In our case, the main queue, which determines which thread to run. Since we need to return a variable from this function, I need to check if we already are on the main thread. If not, I can dispatch a synchronous block execution on the main thread following set up. If we wouldn't check this and run on the main thread regardless, we would have created a deadlock situation. Next up is the persisting store coordinator. We are again storing it in a variable, but implement it set call back to set up the main queue context again afterwards. Now, the main function in this class create stack, which returns a Core Data stack object. We first fetched a ManagedObjectModel from the main bundle and I'm doing this by using a custom function. I can create this function on the bundle using a private extension. ManagedObjectModel will use a guard clause to gracefully fail if the MOND file is missing or corrupted. Back in the other function, we need a new store coordinator with the ManagedObjectModel we just loaded. Then we have to add a persistent store to the coordinator. Since this can throw an exception, I will wrap it in a do catch clause. We want an escalate store with no special configuration and a specific store ul. Which will be up [INAUDIBLE] in the applications documents directory. To fetch that, I'm using another extension. This time, on Core Data stack itself to keep it nice and tidy. We can ask the NSFileManager for a list of URLs of the documents directory and just use the last one. If there is a failure, I will throw a fatal error here. Normally, you should handle this gracefully. Now, it is time to return an instance of our Core Data stack. In the initializer function, we will store the coordinator as an instance variable. If you want to stop here, that's fine. There's just one line of code you will need to add to this class, but I will tell you exactly which one it is as we move along. The persistent queue context is almost the same as the main queue context. So, I can copy the first few lines of the variable and function definitions and just rename them. In this context, we don't care about the main queue. So we can create the context directly with the private queue concurrency type. And also, giving it a name to identify it properly and that is it, context initialized. Now, we have to adapt the rest of the class to use it. The main queue context needs a reference to the parent context. And when we set the persistent store coordinator, we also need to initialize the persisting queue context. Now comes the line that was missing. We need to set the persistent store coordinator on the persistent context. If you would only have a main queue context, you will set it on that. Although it should get set in the initializer as well, I'm making sure it really is by setting the persistent store coordinator here as well. Now we have the SQLite stack, but in some cases, especially for testing, we want to store that is very fast and we don't care about persisting data. This is where an in-memory store comes into play. It is very similar to stack, but we don't need a store URL and we are using the in-memory store type instead. Everything else is the same Now that we have two store types, we should let the stack object know which type it has. I'm going to create an enum and also store the URL within it. Then, I can set it in the initializer. Finally, I'm also going to add an instance accesser for the ManagedObjectModel to the class. The class looks great. We have set everything up, but there is one part that is essential to applications with user data. That is, tearing it all down. So the final feature I'm adding to this class is going to be a way to reset the store. For instance, when the user logs out of the application. We have two types of stores and they need to be handled differently. The in-memory store will only have one persistent store. So we grab it and handle the error if there is no store. Then we can remove it and add a new one with the same parameters that we used before. Since it can throw, we need to catch URL as well. In case of an SQLite store, I'm going to completely remove the coordinator. Since we stored the store's URL in the we can look for it under coordinator. Then we can actively destroy it instead of just removing it. Now, it's time to create a new coordinator and add a persistent store at the same location where the old one was. After handling all the errors correctly, we can set it as the new coordinator and the context will get recreated as well. I know that was a lot to process in an earlier lesson, but it is the heart of Core Data and needs to be done first. If you didn't fully understand it, maybe watch it again at the end of the course when you know more about Core Data. Luckily, you normally write this once and can reuse it in other applications almost unchanged. In the next lesson, we will start with the Core Data model and create ourselves some entities to use in the application. See you there.

Back to the top