written / illustrated by Scott Stevenson
Cocoa has been refined dramatically over the
years, but no one technology has brought such sweeping advances as Core
Data. Using the Core Data framework, you can create a suprisingly
sophisticated application before you write your first line of code.
Your first thought might be that that writing less code means
that you don't have a "real" Cocoa app, but this isn't the case.
Core Data simply provides pre-built classes and tools for data
management. You build your custom code on top of this basic set of
functionality.
Core Data is the result of Apple's extensive experience in development tools.
It's a real, industrial-strength framework, and builds heavily on
technologies introduced in Panther, such as Cocoa Bindings.
Open Xcode 2.0 and Choose File > New Project. The window shown below will appear:
Choose Core Data Application from the Application group and click the Next button.
Name the project BlogDemo. It's important to use this exact name.
Open the Data Model File
In Xcode, open the Models group and double-click the BlogDemo_DataModel.xcdatamodel file, as shown below.
The data modeler window will open but will be blank.
An Entity is similar to a class. In BlogDemo, the Post Entity represents a
post on a blog site. Each Post has a title, a body, a creation date, and an
author.
In a traditional Cocoa application, we would create a Post class, then add
code to make it work. Since this is a Core Data application, we'll
make a Post Entity instead.
An Entity is different from a class in that it doesn't contain code. It's just a description of data. Core Data
uses this description to decide how the application should behave.
Between the top and bottom portions of the modeler window, there's a small
plus button. Click it once to create a new
Entity.
Double-click the default name
"Entity" in the table row and rename it to
Post.
Just as an Entity is similar to a class, a Property is similar to an
instance variable. There are three kinds of Properties, but the only one you need to know about for now is Attribute.
An Attribute holds a single value. An Attribute also has a type
associated with it, such as string, number or date. Unlike an instance variable, there is a fixed list
of types that an Attribute can be. This is similar to how SQL databases have column types.
Make sure the Post Entity is selected the far left table view.
Click
the plus button under the Properties pane and select
Add Attribute from the popup menu
Double-click "newAttribute" in the table view and rename it to
body.
The body Attribute is now part of the Post Entity.
The third section of the modeler window allows you to fine-tune settings on a
particular Attribute. Select the body attribute to display the third section
of the modeler window.
Uncheck "Optional"
Choose
String from the
Type dropdown.
This displays options specific to the String type.
Set the default value to
"Body Text"
Create another Attribute called title. Use the same options as you did for body, but set the default value to "Title" instead:
Next, add an Attribute called creationDate. Choose Date from the type dropdown.
Make sure that the Optional checkbox stays checked in this case:
We now have a Entity, called Post, which has three Attributes: body, creationDate and title.
Every blog post has an author associated with it, so let's create an Author
Entity.
Click the plus
button at the bottom of the Entity table
Rename the Entity to
Author
Add Attributes to Author
Add the two following Attributes to Author:
Create an attribute called
name with a type of
String, and
deactivate the Optional checkbox.
Create an attribute called
email with a type of
String.
The graphical model should look like the one above.
We want to have topics for posts to the blog.
We could add a topic Attribute to Post, but a creating a separate
Topic Entity will give us
the option of making changes to topics without editing each individual post.
Once again, click the plus
button at the bottom of the Entity table
Rename the Entity to
Topic
Add an Attribute to Topic
Add a single Attribute to Topic. Name it title with
a type of String, and deactivate the Optional checkbox.
This may seem like a simplistic Entity, but we can always add to it later.
Let's review what we have done so far.
Launched Xcode, and chose
File > New Project.
We selected
Core Data Application and named the project "BlogDemo".
Created the
Post Entity
• represents a post to the blog
• attributes:
body, title, creationDate
Created the
Author Entity
• tracks who creates each post
• attributes:
name, email
Created the
Topic Entity
• used to organize posts
• attributes:
title
We now have three Entities, but
they have no connection to each other.
An Attribute can only represent a single value, such as a string, a date,
or a number. To create a link between two Entities, we need to use another
kind of Property, called a Relationship. Relationship is a link between two Entities.
We will create a link from Post to Author, so that
our application can keep track of who created a blog post. First, select the Post Entity so that all of its properties are visible in the
center pane.
Click the plus button in
and select
Add Relationship from the popup menu. Note how the Kind column says "Relationship".
Rename the "newRelationship" to "author"
Choose the
Author Entity
from the
Destination dropdown.
The graphical view should now look like the image to the right.
The arrow tells us that the Relationship
is a link from a post to an author.
Just as we want to keep track of the author of each blog post, we want to track all the
posts for each author. We need to create a Relationship from Author to Post.
In other words, the mirror image of the other Relationship.
First, select the Author Entity so that all of its properties are shown in the center pane.
Click the plus button in the center pane
and select
Add Relationship
Rename the relationship to "posts" (plural)
Choose
Post
from the
Destination dropdown
Activate the
To-Many Relationship
checkbox. This means that every author will have
multiple posts.
The graphical model should now look like the image to the right.
Notice how the Relationship
from Author to Post has two arrows. This indicates that it is a to-many Relationship.
We now have both Relationships set up, but it seems odd to have two separate Relationships when
they are just mirror images of each other. We have the Post's Author and the
Author's Posts. Let's make things easier by combining them.
Verify that the Author Entity is still selected so that its Properties
are displayed in the center pane.
Select the
posts Relationship
Select "
author" from the
Inverse dropdown
Notice how the two Relationship lines have been combined in the graphical representation to the right.
An inverse relationship doesn't just to make things more tidy, it's actually used by Core Data
to maintain data integrity.
Virtually all Relationships should have an inverse version. It makes writing an application
much easier and provides you with features you otherwise wouldn't have.
The last
Relationship to create is a link between
Post and
Topic. The following steps are nearly identical to the previous tasks involving Relationships.
Select the
Post Entity and click the plus button under the Properties pane.
Choose
Add Relationship
Rename the Relationship to "topic". Choose
Topic from the
Destination dropdown
Select the
Topic Entity and click the plus button under the Properties pane.
Choose
Add Relationship
Rename the Relationship to "posts" (plural). Choose
Post from the
Destination dropdown
Activate the
To-Many Relationship checkbox
Select "topic" from the
Inverse dropdown
Let's review what we just did. Remember that a Relationship is a link between two entities.
Created a
Relationship from
Post to
Author
• tracks who creates a given blog post
Created a
Relationship from
Author to
Post
• tracks all blog posts by a given author
• to-many relationship
Created the
Inverse Relationship
• combines the other two relationships
Created a
Relationship from
Post to
Topic
• associates a topic with a post
Created a Relationship from Topic to Post
• tracks all blog posts on a given topic
• to-many relationship
Created the Inverse Relationship
• combines the other two relationships
Building a user interface with all the correct sizing attributes is beyond the scope
of this tutorial, so we're going to use a prebuilt NIB file.
Select
MainMenu.nib in Xcode and press the
Delete key. Choose
"Delete References & Files" from the sheet that appears
Download either the version of the NIB for Xcode 2.2 or later, or download the NIB for Xcode 2.0/2.1. Download only one version. Once you have the file, double-click it to decompress it.
Drag the new MainMenu.nib into
the Xcode project's
Resources group,
above the other items in that folder. Accept
the default settings for adding the file to the project.
Double-click the new MainMenu.nib file in Xcode to
open it in Interface Builder
The contents of the NIB file is shown below.
The
Posts table. The blog posts
will be listed here by title.
Detail view. This is where the contents
of the
selected Post will appear.
The
Author configuration table.
Add, edit and delete Authors here.
The
Topic configuration table
The NIB document window looks like this:
The blue cube is BlogDemoAppDelegate, which replaces the
MyController class found in conventional Cocoa applications. This class is
automatically created and instantiated in MainMenu.nib when you create a Core Data
application.
We will use Cocoa Bindings to populate the user interface. This is
an easy choice because the Core Data framework is integrated with Cocoa Bindings.
To use bindings, we need to add one NSArrayController instance for each Entity.
Bring up the Controllers palette in Interface Builder, which is shown to the right.
The NSArrayController is the icon with a series of small green boxes in
a single-file line.
Drag the NSArrayController icon
from the pallete to the document window
Double-click the title to
rename it to "Posts"
Select the Posts
NSArrayController
and bring up the attributes section of Interface Builder's
inspector window
Click the
Entity radio button
Type "
Post" into the
Entity Name text field
Activate the "
Automatically prepares content" checkbox
Add Bindings
Choose "Bindings" from the dropdown at the top of the inspector window to setup bindings
for the array controller.
Bind
managedObjectContext to
BlogDemoAppDelegate
Set the
Model Key
Path to "managedObjectContext"
Make sure the
Bind checkbox is
activated
Repeating the previous steps, create and configure NSArrayControllers for the other
entities.
For the Author Entity:
Create a new
NSArrayController and name it
"Authors" (plural)
Select
Entity for the mode, set the Entity Name to
"Author" (singular)
Activate the Automatically prepares content checkbox
Bind managedObjectContext to
BlogDemoAppDelegate
Set the Model Key Path to "managedObjectContext"
Make sure the Bind checkbox is activated
For the Topic Entity:
Create a new
NSArrayController and name it
"Topics" (plural)
Select
Entity for the mode, set the Entity Name to
"Topic" (singular)
Activate the Automatically prepares content checkbox
Bind managedObjectContext to
BlogDemoAppDelegate
Set the Model Key Path to "managedObjectContext"
Make sure the Bind checkbox is activated
The end result should look like this:
We're going to go through this section relatively quickly, as these concepts are
covered extensively in the Cocoa Bindings tutorial.
Bind the Table
Posts Table Column
Bind to: | Posts |
Controller Key: | arrangedObjects |
Model Key Path: | title |
Select the column of the Posts table. You may have to double-click the area several times to get it to appear and select it. After you have it selected, bring up the Bindings section of the Inspector Window. Bind the table column's value as shown in the table to the right.
Connect the Buttons
Hold down the Control key and drag a connection from the plus button to the
Posts array controller, approving a connection to the
add: action
Control-drag a connection from the minus button to the
Posts array controller, and approve a connection to the
remove: action
Bind the Text Boxes
Bind the "value" slots of the title and body text boxes as follows:
Title Text Field
Bind to: | Posts |
Controller Key: | selection |
Model Key Path: | title |
Body Text View
Bind to: | Posts |
Controller Key: | selection |
Model Key Path: | body |
In all three cases, the Model Key Path is the name of the Attribute that
we want to populate the control with.
Here's a quick review of the last section.
Added a
prebuilt MainMenu.nib file to the project
Added and configured
three
NSArrayControllers, one for each
Entity
in BlogDemo
Bound the main
table view's column to the
Posts NSArrayController
Connected the buttons to the
add: and
remove: actions on the
Posts NSArrayController
Bound the
title and
body text boxes to their respective
Attributes values
Compile and run BlogDemo. Even though we didn't write any code, you can add, modify
and delete records. If you quit and re-run the application, all the
data should return.
Some things are clearly missing, though. The Author and Topic dropdowns don't do anything
yet. The accessory panels are empty too.
In order to assign an Author and Topic to each Post, we need to setup the bindings for the dropdown boxes.
Bind the content, contentValues and selectedObject keys for each dropdown, as shown in
the tables below. Note that selectedObject key binds to a different controller
than the other keys.
Author Dropdown: content
Bind to: | Authors |
Controller Key: | arrangedObjects |
Model Key Path: | leave empty |
contentValues
Bind to: | Authors |
Controller Key: | arrangedObjects |
Model Key Path: | name |
selectedObject
Bind to: | Posts |
Controller Key: | selection |
Model Key Path: | author |
Topic Dropdown: content
Bind to: | Topics |
Controller Key: | arrangedObjects |
Model Key Path: | leave empty |
contentValues
Bind to: | Topics |
Controller Key: | arrangedObjects |
Model Key Path: | title |
selectedObject
Bind to: | Posts |
Controller Key: | selection |
Model Key Path: | topic |
The idea here is to bind the selectedObject key of a dropdown to the name of a Relationship.
Post has a relationship called author, so we bind selectedObject to a model key path of "author". Post also has a Relationship called topic, so we bind the selectedObject of the other dropdown to "topic".
Table Column: value
Bind to: | Topics |
Controller Key: | arrangedObjects |
Model Key Path: | title |
Open the
Topics panel, and select
the table column
Bind the
value key as shown in the table to the right
Control-drag a connection from the plus button to the
Topics array controller, approving a connection to the
add: action
Control-drag a connection from the minus button to the
Topics array controller, and approve a connection to the
remove: action
Authors Panel
Name Column: value
Bind to: | Authors |
Controller Key: | arrangedObjects |
Model Key Path: | name |
Email Column: value
Bind to: | Authors |
Controller Key: | arrangedObjects |
Model Key Path: | email |
Now, repeat the same process for the Authors panel.
Bind the
value key of both the name and email columns as shown in the table to the right
Hold down the Control key and drag a connection from the plus button to the
Authors array controller, approving a connection to the
add: action
Control-drag a connection from the minus button to the
Authors array controller, and approve a connection to the
remove: action
Compile and run BlogDemo. Now that we added bindings to the dropdowns
and the acessory panels, you can create Authors and Topics and
assign them to posts.
If you restart the application, you'll see that all
the data is still intact.
Locate the BlogDemo data file in the Finder. It is placed inside your home directory,
in /Users/<yourname>/Library/Application Support/BlogDemo/BlogDemo.xml
Drag the XML file to the Xcode icon in the dock to view it.
The format is very straightforward. The markup is clear and the element
names reflect Core Data terms. If you'd like to see an
overview of the file structure, open /System/Library/DTDs/CoreData.dtd.
Your application should not edit the data file directly. Always use the
Core Data API to make changes.
Core Data applications use XML data stores by default, but you may want to use a SQLite or
even a standard Binary data store. This is easily done. Open the BlogDemoAppDelegate.m file.
The data store is defined in the -managedObjectContext method, which is generated automatically when
you create the project.
BlogDemoAppDelegate.m
url = [NSURL fileURLWithPath: [applicationSupportFolder
stringByAppendingPathComponent: @"BlogDemo.xml"]];
...
if ([coordinator addPersistentStoreWithType: NSXMLStoreType
configuration: nil
URL: url
options: nil
error: &error])
...
Using a SQLite Store
Change
BlogDemo.xml to
BlogDemo.blogdemodoc
Change
NSXMLStoreType to
NSSQLiteStoreType
Using a Binary Store
Change
BlogDemo.xml to
BlogDemo.blogdemodoc
Change
NSXMLStoreType to
NSBinaryStoreType
Core Data projects default to using .xml, .sql and .binary, but it's strongly recommended that
you change this to an extension unique to your application, such as .blogdemodoc for BlogDemo.
Also, keep in mind once you change the store type to something other than XML, you will not be able
to make changes to your data model as easily.
In particular, the tables for the SQLite store are
created once, so if you change your entities after the file has been created, you will see
errors about invalid columns. At that point, you must delete the SQLite data file so it can be
recreated. It's generally easier to use the XML store type during development.
For more information on changes to data models, see this page.
Data Store Differences
The XML store is most useful during the prototyping phase, when you
want to quickly glance at the data that's being written to disk. It doesn't scale well, though.
The Binary store is generally faster than the XML store, but can't be easily read in a text editor.
The SQLite store is generally the fastest and most scalable store.
The Binary and XML stores
have to load the entire contents of their data files at startup, but the SQLite store can load and
unload data as needed. This can significantly shorten startup time and conserve memory.
You can inspect the contents
of the data file using the command line tool, found at /usr/bin/sqlite3.
The only
drawback of the SQLite store is that it appears to be somewhat slower than the Binary store during
an initial save.
This tutorial was designed to get your first Core Data app running as quickly as possible.
If you'd like to better understand how Core Data is architected, take a look at the Core Data Class Overview article.
As always, let us know what you think about the tutorial.
Further Reading