Core Data Tutorial - Part 1

This is the first in a series of tutorials designed to help you learn Core Data for the iPhone. Core Data is an extremely flexible framework, but it's important to note that with that flexibility comes a few restrictions as well. So, let's start by talking about what Core Data is, what it isn't and why you want to use it for your applications.

What It Is

Simply put, it's a framework that makes working with application data much easier. And I'm not talking about a single list of values that never change - Core Data's power would be wasted on something so simple. No, Core Data's strength lies in enabling you to create rich and robust applications that have data as the focal point. A good example of where Core Data will shine would be the Contacts application. And it gives you a lot of power - for free. Just as important as what it gives though, is what it doesn't give you.

What It Isn't

Core Data, first and foremost, is not an entry level technology. If you know nothing about programming for the iPhone or in general, go take some time to learn the basics before you dive head first into Core Data. You'll thank yourself later when you don't have to go to the internet every time you want to push a new view controller or you can't figure out why you're leaking memory like a sieve. Core Data is also not for control freaks. If you want to use sqlite, but can't stand the thought of not being able to write your own sql statements - don't even bother with Core Data. But, if you want to make your life slightly easier and not worry about all that, keep reading.

Why You Should Use It

There are a bunch of great reasons to use Core Data, so I'll highlight just a few. I'm sure you're tired of reading and want to get on to coding anyway. First is a GUI-based editor for defining your model objects. You can see each Entity, it's attributes and the relationships it has to other Entities. The GUI editor even allows you to set default values for attributes. You also get versioning and migration for your data store. So there's no more fretting about how to preserve the users data now that the application is live and you need to add another column to a table. And this is just a small scratch on the surface, the real power is under the hood. So enough talk already, let's start a project!

Because Recipe Apps Suck

We're not going to write the standard recipe app. Why? Because they suck. I graduated from Le Cordon Bleu in Las Vegas, so I know a small thing or two about cooking and recipes. And I know that every tutorial I walk myself through makes a mockery of something I love and it makes me cringe. And because I'm writing one that doesn't suck. So what are we going to write then? The only thing that has the potential to be worse than a recipe app - a crummy todo app of course!

GetRDone

Oh ok, we won't make this thing crummy. But remember this - the point of these tutorials isn't to show you how to customize the look of your app, we'll save that for later. This is all about Core Data. Make sure you have installed the developers tools for iPhone OS 3.0. Off we go!
  • Open Xcode
  • Go to File -> New Project or simply Shift + Command + N
  • Under iPhone OS choose Application, then Navigation-based Application
  • For options, make sure 'Use Core Data for storage' is checked
  • Click Choose in the bottom right hand corner
  • Name the project GetRDone (Hey, it might be a retarded name, but we're going to have some fun with this)
  • Bask in the glory of what Xcode has just created for you.
When your done basking and looking around and whatever else you're going to do, expand the resources folder and select GetRDone.xcdatamodel. This is the GUI for defining our data model. You'll notice there is one entity already created for us called 'Event'.

The ToDo Entity And It's Properties

Select the event entity either by clicking the bubble on the graph paper or selecting it from the pane on the left. Now on the right-hand side you should see three values: Name, Class and Parent. Change the name to ToDo.

In the middle pane you will see one property called timeStamp. I'm a big fan of timeStamps, so we'll keep this. In the bottom left hand corner of the middle pane you'll see some buttons. Click the plus button and choose 'Add Attribute'. Call the Attribute 'name' and change it's type to String. Also keep the box on the right for 'Optional' checked. Create two more attributes:
  1. detail - String - Optional checked
  2. dueDate - Date - Optional checked
Save the file. It should look like this:

Ok. I've created a new group that I named 'Data Model & Classes' - this helps keep me organized. I put my data model file in that group. Now, choose the model file again, highlight the ToDo entity by clicking on the bubble in the graph paper, then go to File -> New File or simply Command + N. Go to iPhone OS -> Cocoa Touch Class and choose Managed Object Class.

Click Next twice, then click Finish. You just generated the .h and .m files for the ToDo entity. These are essential for accessing the properties of our entity later on.

The Small Amount Of Glue Needed

Now, select RootViewController.m. Scroll down to the fetchedResultsController method and change @"Event" to @"ToDo". Ok, click Build and Go. The application runs fine. Clicking plus adds items and shows their timestamp. Clicking edit and deleting them deletes them... until we delete the last item, then the program crashes. If you check the log you'll see that there is a conflict with the number of sections being different than what it was before. Let's fix that and call it a day. Stay in the RootViewController.m file and scroll up to the numberOfSectionsInTableView method. Change it and the numberOfRowsInSection method to look like so:


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger numberOfSections = [[fetchedResultsController sections] count];

if (numberOfSections == 0) {
numberOfSections = 1;
}

return numberOfSections;
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger numberofRows = 0;

if ([[fetchedResultsController sections] count] > 0) {
id sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
numberofRows = [sectionInfo numberOfObjects];
}

return numberofRows;
}

Build and Go and you'll see that everything now works the way it should (no crashing). What we did was told the table view to always return at least one section, that way we have no conflicts when we delete that last item and technically we no longer have any sections. We also had to make an adjustment to the rows method to let it know that unless we have at least one section, we shouldn't be trying to count how many rows there are.


Summary


I'll go over the NSFetchedResultsController in more detail throughout this series so that you understand how it works and why we are doing what we are doing. In the next post I'll show you how to fully add a ToDo.