Join Simon Allardice for an in-depth discussion in this video Creating managed objects, part of Core Data for iOS and OS X.
So we can take modeling further than that, but let's not. Right now, let's take what we know, make a simple managed object model, and see how to use it. Because the entire point of defining these entities is that we are going to instantiate managed objects based on those entities, we're going to use those objects, and then we are going to get Core Data to save them. And that's what we are going to do in this next section: create, use, and save managed objects. And by the time we're done here, we'll naturally run into a few more pieces of the Core Data puzzle, things like context and store coordinators, and we'll also answer the question if we are saving these managed objects, where do they actually go, where do they save to? So let's get to it.
In Xcode, I am going to create a new Cocoa Application this time, so under OS X select Cocoa Application. If you have never done Cocoa Apps, don't worry, you will be able to follow along just fine. I am going to call it SaveDemo, and the only thing I am going to pay attention to here is just looking at the Company Identifier for a moment. I have got com.company, that's fine. It's not important what it is. I just want to remember it, it will come in handy later. I am now going to have an App Store Category, Document-Based Application is unchecked, I want Core Data checked, and Use Automatic Reference Counting checked, and that should do it.
Click Next, and I'll just save this to my Desktop. Now when I make a Cocoa Application with the Core Data check box selected, I don't get a sample application the way I did with a Master-Detail iOS template. If I go ahead and run this, there is nothing here. We need to add everything that's necessary here, and I'm going to make just a simple one-button application. But if I go back into Xcode, I see that I do have a data model file here in this project, although it's completely empty, there is no entities in it, and I do have some of the Core Data code has actually been added into my app delegate. We'll see that a little later on.
So I am going to jump into the model here and just add a new entity. I'll call this Course, and I'll just give it three attributes to be simple here, title, author, and I'll call it release date. They are all undefined right now. That's not acceptable, I want the title and author to be strings. One thing I can do here is using Command, I can click on title, then Command-click on author and with both of these selected in the Inspector change their attributes to string.
That can come in handy when you've got four or five at a time you want to do. Release date, I'll just change manually to date, pretty much as simple an entity as I can imagine here. Now what I am going to do is jump over to the interface. So I am going to click on the MainMenu.xib file, and I want the main Window part of this so I can select that in the dock, either in the minimized or the expanded view, I just want a Window so that I see it here. From my Object Library and my Inspectors, I am going to drag on a Push button and just give this a name, shrink down the window because it is pretty simple.
This button is going to instantiate a managed object based on that entity I've just written, and then change the properties of that object and make sure it's saved. I am just going to give myself a bit more screen real estate here and switch into Assistant View, and I should be bringing up my AppDelegate on the other side here. If you are coming from iOS, know that in Cocoa I don't have to have a formal separate view controller class file. I can just have all of my code in the AppDelegate for a simple app like this. So I am going to make this button just call an IBAction here, so I'll Ctrl-click from the button over down here in the AppDelegate and let go.
I want to make sure I'm creating an Action, not an Outlet, and I'll call it createObject and hit Connect. That's everything I need to do in the header for now. I am just going to give myself my regular editor back and jump into the implementation file for AppDelegate. I'll use the jump bar to find that new method I created, createObject. Here is where I am going to write all the code that I need. Now normally when we instantiate an object, we are going to use alloc init or some variant of that, perhaps a helper method of a class, where we'd have a couple of pieces of information, the name and the type of the object pointer we are creating and the name of the class we are creating it from.
But when I am instantiating a managed object, which is what I want to do right now, we need a little different information. Well, first, I do just want to create a managed object pointer. So I am going to start typing NSManagedObject. There we go. And I'll give it a name like myMO for my managed object. But I don't just want to say alloc init, because I need to say this managed object is based on a particular entity that we've described in our model, the Course entity that I just wrote.
And to do this, Core Data requires not just the entity name but another piece of information. So here's the classic most convenient way to create a managed object from a named entity. I am actually going to use the NSEntityDescription class, which has a method called insertNewObjectForEntityForName: inManagedObjectContext, so let me just enter that one in and split it across a couple of different lines so I can talk about it a bit easier here.
There are other ways to create a managed object. This is the simplest. Now it might look a little strange because to create this NSManagedObject we are using a factory method but not of the NSManagedObject class, but a different class, NSEntityDescription. And what we are saying is make an NSManagedObject based on this particular entity, which I can name with a string here, call it course, and we need this extra piece of information, because as soon as I create this managed object, and this is true for every managed object, it needs to be in a container, a named area that's called a context or a ManagedObjectContext.
We'll talk more about what this container exactly is in just a minute. For now, let's just do it. I have a property of this class called managedObjectContext, and that's what I need here. So this method that we are using is saying make an NSManagedObject based on this entity and make it in this container, this ManagedObjectContext, and return me a reference to it. We now have a managed object. And I want to set the values of this. Well, in the entity I just made, I had things like title and author and release date, so it would be nice if I could say something like myMO.title equals, but I don't have that.
Unfortunately, that won't work yet because I created this as a NSManagedObject reference, and that's a generic class, and it doesn't know about custom properties. We'll see a little layer how I could get some custom properties and have .title and .author and .subject or whatever else I had directly accessible. But in the meantime, what I have to do is use key-value coding to set those properties. It's a little clunkier, but it will work. So I am using the SetValue: forKey method, giving it a value such as Core Data for the key, and this is a string, title.
And this refers to the name of the attribute that I defined in the Entity. Next, I'll set the value for the author attribute, and finally, I'll set a value for the release date attribute, here this is going to be an NSDate, and right now that's it. I am going to run this, Run. I am going to go ahead and when this opens, I am going to click the button and click the button and click the button.
Well, I shouldn't really expect to see very much as what we are doing, we are doing completely silently, there's no alerts, no messages, no log messages, and for now that's okay. So here's the question, is this object being created? Yes, in fact, every time I click this button I'm creating another one. Now is it being saved? Well, no, not yet, but they will be in just a second. So I have created several objects here, and I'm now going to quit out of this app.
I can either use the menu, or I can just hit Command+Q. But at that particular moment, when I terminated the application those managed objects were saved by Core Data. Well, why and how and prove it. See, there is nothing weird or automatic going on, you see? In a default Cocoa Application with Core Data when you terminate an app, it's going to call the usual application terminate event here, we have got applicationShouldTerminate, so I am going to jump to that method, here we go, and there are some code that's been provided for us to tell Core Data to save. It's actually this line here.
Now we'll explore this code in depth in just a bit. There is a bunch of code here, most of it is actually just dealing with generating alert messages if there's a problem. But I do see the actual word save here, and here is where we are saving those objects. And Apple have put this code in the applicationShouldTerminate method because that's the way they've been moving towards over the last few versions of OS X, that if you just quit from an application, it won't prompt you to save, it will just automatically save your work. Now we will be able to also choose to save anywhere else we want, anytime we want.
We don't have to wait until the application terminates, it's just already here in the template. But first, how can I prove this work? Let me show you where Core Data is going to save data for a Cocoa App. I am going to open up a Finder window, actually here what I need to do is go to my Go menu and say Go to folder, go to my home folder Library, and I am going to jump into Application Support, and here's where it was important that I looked at my com.company as the name here because this is the Application Support folder that's been created for this application, com.company.project name, which was SaveDemo.
If I open that up, I can see I have got this SaveDemo.storedata file. This is what's called the store file or the persistent store file. Our managed objects have been serialized, encoded, archived, whatever term you want to use by Core Data into this file. And I can prove that. You see, in Core Data, the internal structure of this store file can take several different forms, you can tell Core Data what format you want it to use when saving. Although, until you have a reason to change it you just leave it alone, leave it at the default.
And in a default Cocoa Application using Core Data, the format of this is actually XML, and that's readable. So I'm going to Ctrl-click or right-click on this and tell it to open up with--nothing is showing up right now, so I'm going to select Xcode and click Open. And this is our persistent store file, or what's inside it. That doesn't look completely obvious here. We've got some strange names like NSPersistentFramework and lots of keys, but if I scroll down, this looks like the kind of thing I'd expect to see.
We've got an XML format here of an object of type Course with three attributes inside it for title, release date, and author, and this has all been taken care of by Core Data simply because we described those entities. Now I am not going to change anything about this. I just wanted to prove that it was actually happening that this information was being saved into our file system because I'd click that button several times. Now for an iOS application, the default storage format is actually using SQLite, but it is still storing a data file to the iOS file system just like this version is.
It's just not as easy to navigate and actually demonstrate that. Now of course, these values can be read back in, but we are not actually worrying about that for now. That's going to come in the next phase. What we need to worry about and understand a little better is the object that we had to use to make this work, the ManagedObjectContext.
- Understanding Core Data and object persistence frameworks
- Creating a Core Data project
- Exploring data modeling
- Creating entities, attributes, and relationships
- Creating managed objects
- Fetching in Core Data
- Implementing undo and redo support
- Creating a Core Data Cocoa app without code
- Responding to validation issues
- Converting store types
- Preloading default data
Skill Level Intermediate
Q: In the CoursesViewController.m file, I receive a message that the method dismissModalViewControllerAnimated is no longer supported by Apple. Is there a newer method to use?
A: You may have received the warning that a call to the dismissModalViewControllerAnimated: method is now deprecated.
This is a recently deprecated method, so the code _will_ continue to work using that call, but to remove the warning, change the code from:
to the current recommended version:
[self dismissViewControllerAnimated:YES completion:nil];
It should take care of that message.