TweetFollow Us on Twitter

The Road to Code: Writing Even Less Code

Volume Number: 24
Issue Number: 12
Column Tag: The Road to Code

The Road to Code: Writing Even Less Code

Introduction to Core Data

by Dave Dribin

Core Data

Our topic this month is Core Data, a technology introduced in Mac OS X 10.4 by Apple to help reduce time spent writing code. Interface Builder allows you to setup view classes with out writing any custom layout code. Cocoa bindings allow you tie together the view and the model without writing any custom controller code. What's left? The model. Core Data is an attempt to allow developers to write model classes without writing any code.

Core Data was introduced in Mac OS X 10.4 and heavily enhanced in OS X 10.5. In a nutshell, Core Data provides automatic object persistence and object lifecycle management. The phrase object persistence is a fancy way of saying "saving objects to a file," similar to archiving. Core Data provides more, though. It allows you to find and filter objects and provides integration with the user interface, such as automatic undo support. It can optionally store your objects to a single-user relational database for increased performance.

It is common to think of Core Data as an object-relational mapping (ORM) tool. ORM tools automatically persist objects to a relational database. ORM tools allow you to save, find, and update objects using SQL statements. SQL is the language of relational databases, and ORM tools typically generate the SQL select, update and insert statements for you. Some popular ORM tools in the Java world are Hibernate and Enterprise Java Beans (EJB), and Active Record is the ORM tool for Ruby on Rails.

However, there is one very important difference between most ORM tools and Core Data. Most ORM tools are designed for multi-user and high-concurrency environments, such as the backend of web application. Core Data is designed with the single-user desktop application in mind. You'll see some of the ramifications of this as you delve into the architecture and use cases of Core Data. For example, Core Data does not require use of a relational database.

A Bit of History

Before digging into the details of Core Data, it is useful to understand where it came from. It turns out that Apple has it's own web-based ORM tool as part of WebObjects. WebObjects is a complete web application framework. It started life way back in the early 1990s at NeXT written in Objective-C and was later rewritten in Java. When Apple acquired NeXT, mainly for Mac OS X, it also acquired WebObjects. Today, Apple uses WebObjects for its online store and the server side components of the iTunes Music Store. It is also available for free for developers to use and has quite a strong, albeit small, following.

WebObjects includes an ORM tool called Enterprise Objects Frameworks, or EOF. It allows automatic persistence of Java model objects to a relational database. As far as ORM tools go, EOF is quite powerful and feature filled. However, since it is now only available in Java, it is not something that can be used directly by Cocoa applications.

Apple used its experience with EOF to design Core Data. It took many of the concepts of EOF and pared them down for single-user applications. There is no direct link between EOF and Core Data, just a foundation of ORM experience. Unfortunately, not all of the features of EOF were ported to Core Data, so sometimes we are left wanting.

Terminology

Core Data introduces some new terminology, mainly borrowed terminology from the database world. Databases are modeled using the entity-relationship model, thus Core Data objects are also modeled using the entity-relationship model. But don't fret, most of these terms correlate to terms you already know.

In an entity-relationship model, a class is called an entity. And just classes contains instance variables, entities contain properties. There are two kinds of properties: attributes and relationships. Attributes are properties consisting of simple data types, such as strings, numbers, and dates. Relationships are properties that link together multiple entities. We won't be covering relationships in this article.

Okay, enough of the jibber jabber, let's get on to writing a Core Data application.

Core Data Version of Rectangles

We are going to convert the Rectangles application that we have built up over the last few months to a Core Data application. Using Core Data will allow us to remove almost all of our code. We can't quite get rid of all of it, but the reduction is still dramatic.

We're going to start off my creating a new project. Create a Core Data Document-based Application from the New Project window, as shown in Figure 1.


Figure 1: New Core Data project

This project looks similar to standard document-based applications, but it has an additional group in the Groups & Files list called Models, as shown in Figure 2. In that group is a file named MyDocument.xcdatamodel. This file extension is short for "Core Data data model" and represents the entity-relationship model for our application.


Figure 2: Groups and Files

We're going to dive right in by working on our data model first. We want to create a rectangle entity with width and height attributes. If you double click on the data model, you will be presented with what is known as the Core Data modeler, as shown in Figure 3. We will design our data model graphically without writing any code or text.


Figure 3: Core Data modeler

Click the + button in the Entity pane to create a new entity. Use the detail view on the right side of the window to rename the entity to Rectangle, as shown in Figure 4.


Figure 4: Rectangle entity

Next, add a new attribute by making sure the Rectangle entity is still selected and click the + button in the Property pane. Rename the attribute to width, uncheck the Optional checkbox, change the Type to Float, and set the Default Value to 0. Now repeat this process to create a height attribute. The result should look like Figure 5.


Figure 5: Height and width attributes

In the bottom half of the window, you will see a graphical representation of our entity-relationship model, as shown in Figure 6. This graphical representation is called an entity-relationship diagram or ER diagram. It is common to model relational databases using an ER diagram, again showing the database roots of Core Data.


Figure 6: Rectangle ER diagram

With that, we're done with our data model. We can now save, load, and use Rectangle entities in our application. But, we haven't written any code, yet. How can you use an entity without any code? Core Data provides built-in classes to make this easy.

Core Data Class Architecture

When you created the Rectangle entity, you'll notice that one of the fields is named Class and, by default, is set to NSManagedObject. Every instance of a Core Data entity is represented by an instance of NSManagedObject. In a way, it is similar to how NSObject is the root class of all Objective-C classes. However, you do not need to subclass NSManagedObject to use it. So how do you access the properties? Using key-value coding.

When an NSManagedObject, or managed object for short, gets instantiated, it's initialized for a particular entity. The managed object reads your data model and allows you to access the attributes using the standard KVC valueForKey: and setValue:forKey: methods. For example, say we have an instance of NSManagedObject representing our Rectangle entity. You would access the width attribute like:

    NSManagedObject * rectangle = ...;
    NSNumber * width = [rectangle valueForKey:@"width"];
    NSLog(@"Width is: %f", [width floatValue]);

But for now, we do not need to use our entity from code. We will be using Cocoa bindings to connect managed objects to the user interface, which uses KVC.

Another important class in the Core Data architecture is NSMangedObjectContext, or managed object context. You use the managed object context to find, create, and delete managed objects. Every managed object instance is owned by the managed object context that created it. The managed object context also contains unsaved changes. Thus, when the user makes their edits, these changes occur only the managed object context. To save these changes to disk, you tell the managed object context to save.

The final important class in the Core Data architecture is NSPersistentStoreCoordinator, or persistent store coordinator. A persistent store coordinator links a managed object context to a specific file on disk, called a persistent store. There are three types of persistent stores that defines the internal file format on disk: XML, binary, and SQLite. The XML persistent store is nice because you can use any text editor to examine the contents. The binary persistent store is faster and more efficient, but it is a proprietary file format. The SQLite persistent store type is the most interesting.

SQLite is a full relational database system that is self-contained and serverless. Most relational database systems, like PostgreSQL, MySQL, or Oracle, require a separate server process to run in the background. A server-based relational database requires a lot of extra setup and can be a little difficult to maintain. SQLite on the other hand is linked in as part of your application and the database is stored in a single file on disk. You access the data using standard SQL queries. SQLite cannot handle highly concurrent multi-user applications like busy web applications, but it is perfect for a single user application, like an OS X application.

When Core Data uses a SQLite persistent store, it works very much like traditional ORM tools. It generates SQL statements to create, save, and find objects in the database. Core Data does not allow you to interact with SQLite directly, though. It hides all the SQL from the programmer.

The benefit of using a SQLite persistent store type is that it is much more efficient than the XML and binary types. Only the objects that you are using get loaded into memory. Thus if the user's data contains a large amount of entities, SQLite will only load the objects the user is using, saving memory. Also, finding and filtering managed objects are more efficient since Core Data can use SQL to find the objects. Databases are designed to perform searches very efficiently.

Generally, you use the XML persistent store type during the early stages of development, because the file can be read with a text editor. However, SQLite is often the best case for a released application since it is more efficient. There are some subtle differences between the persistent store types, so if you do change persistent store types, be sure to thoroughly test after the changes. Also, if you change the type, your application will not be able to load previously saved files.

I admit that the Core Data architecture is somewhat complex. However, if you are writing a document-based application, your life is simplified. Apple provides an NSDocument subclass for document-based applications that use Core Data called NSPersistentDocument. The NSPersistentDocument takes care of setting up the persistent store coordinator and managed object context for you. When you save your document, it saves the context to the user's file. Since we chose a Core Data Document-based Application, our MyDocument class is automatically a NSPersistentDocument.

Configuring the Document Type

Let's now setup the document type for our application, so we can save and open files. Double-click on the Rectangles target to open the Info window for this target. You'll notice there are three document types already present, one for each of the different persistent store types. We'll use SQL type, so delete the other two. Change the Name to Rectangles Document and the extension to rectanglescd. We already used the rectangles extension in our previous, non-Core Data application, so we want to choose a different one to avoid a conflict.

Also change the Identifier to something unique. All of these changes are summarized in Figure 7.


Figure 7: Document types

Laying out the User Interface

We're now going to switch to Interface Builder to layout the interface and setup our Cocoa bindings. Remove the initial text label, and add a table view. Rename the columns to Width and Height. Drag number formatters on each of the columns and set their Style to Decimal. Now add two buttons underneath the table, one named Add the other named Remove. Setup the autosizing appropriately. The final result should look like Figure 8.


Figure 8: Initial window layout

Next, we're going to create an array controller for our Cocoa bindings. Drag an array controller from the Library to our xib document window, and rename it to Rectangles, as shown in Figure 9.


Figure 9: Array controller

Now setup the array controller's attributes to match Figure 10. You need to set the Mode to Entity, set the Entity Name to Rectangle, and check the Prepares Content checkbox. This puts the array controller into Core Data mode and will hold instances of NSManagedObject of the given entity name.


Figure 10: Array controller attributes

Our array controller needs a managed object context to fetch and edit managed objects. Remember that NSPersistentDocument sets up a context for each document. The File's Owner for the MyDocument.xib is our MyDocument subclass of NSPersistenDocument. We can bind the array controller's Managed Object Context to the managedObjectContext of File's Owner, as shown in Figure 11.


Figure 11: Array controller bindings

With our array controller in place, we can now bind the Width and Height columns. Just as we did for our non-Core Data application, bind the Width column to the Rectangles array controller. Set the Controller Key to arrangedObjects and the Model Key Path to width, as shown in Figure 12. Make similar bindings for the Height column, except using height as the Model Key Path.


Figure 12: Width column bindings

Finally, hookup the Add button to the Rectangles array controller's add: action and the Remove button to the remove: action. Bind the Add button's Enabled binding to the canAdd Controller Key and bind the Remove button's Enabled binding to the canRemove Controller Key.

At this point, we have a fully functional application. You should be able to add and remove rectangles from the table using the buttons, and you should be able to save and open documents. Furthermore, you will notice that you get dirty document and undo support. Now I'd like to remind you again that we haven't written any code. This is the promise of Core Data.

Of course our application is missing a few things from our non-Core Data version. First, the default width for new rectangles was 15 and the default height was 10. In our non-Core Data version, we created our own subclass of array controller and overrode the newObject method. We can certainly do that, but for now, I'm going to just change the default values in the data model, to keep with the "no code" theme.

The other major part missing is the area and perimeter table columns. Unfortunately, we are going to have to create some code to get these working. Because these are computed properties, we cannot add them in the data model. We're going to have to create a subclass of NSManagedObject for our Rectangle entity. Fortunately, Xcode can give us a head start. Make sure that your data model is selected in the Groups & Files list and click on File > New FileÉ to create a new file. Click on Managed Object Class in the New File window, as shown in Figure 13.


Figure 13: Generate managed object

Click Next as the Location and Target defaults should be correct. In the final screen, make sure that the Rectangles entity is selected as shown in Figure 14 and click Finish. This will create Rectangle.h and Rectangle.m files. You may want to move them in the Models group along with your data model file so you remember that they are managed objects.


Figure 14: Generate Rectangle class

If you look at the header, you'll see just a couple of @property declarations. There are no instance variables. Remember that the values are actually stored by the NSMangedObject superclass using KVC, so we don't need any instance variables in our subclass. These properties provide convenient, type-safe access to the entity's properties. The implementation file uses the @dynamic property keyword instead of the @synthesize. This is again because there are no instance variables.

Note that the width and height are of type NSNumber, even though we set them as Float in the data model. Core Data only deals with objects. If you want to get the actual float value, you need to use the floatValue method of NSNumber. Conversely, if you want to set the float value, you need wrap a float in an NSNumber:

    // Get the width as a float
    float width = [rectangle.width floatValue];
    // Set the width from a float
    rectangle.width = [NSNumber numberWithFloat:30.0];

Now that we have a real class, we can add our area and perimeter properties. Just as in our non-Core Data application, they are read-only. Listing 1 shows the full header with the property declarations:

Listing 1: Rectangle.h

#import <CoreData/CoreData.h>
@interface Rectangle :  NSManagedObject  
{
}
@property (retain) NSNumber * width;
@property (retain) NSNumber * height;
@property (readonly) float area;
@property (readonly) float perimeter;
@end

I used float here because it is easier to deal with primitive types when doing math. The implementation of these properties is similar to our non-Core Data application. We do have to convert the width and height to floats so we can perform math on them, though. The key-value observing methods are the same, however. Listing 2 shows the full implementation file.

Listing 2: Rectangle.m

#import "Rectangle.h"
@implementation Rectangle 
@dynamic width;
@dynamic height;
- (float)area
{
    return ([self.width floatValue] *
            [self.height floatValue]);
}
+ (NSSet *)keyPathsForValuesAffectingArea
{   
    return [NSSet setWithObjects:@"width", @"height", nil];
}
- (float)perimeter
{
    return ((2*[self.width floatValue]) +
             (2*[self.height floatValue]));
}
+ (NSSet *)keyPathsForValuesAffectingPerimeter
{
    return [NSSet setWithObjects:@"width", @"height", nil];
}
@end

And that's all the code we need. Now, we just need to add the remaining elements to the user interface.

Creating the Final User Interface

Switch to Interface Builder to complete the changes. Add two more columns to the table view and label them Area and Perimeter, respectively. Make sure they are marked as uneditable and add Decimal number formatters to both. Bind them to arrangedObjects.area and arrangedObjects.perimeter, respectively, of the Rectangles array controller. Figure 15 shows the bindings for the Area column.


Figure 15: Area column bindings

Add four labels at the bottom left side of the table for the Total Area: and Total Perimeter: fields. Two will be used for static text and two will be used for the actual values. For the two used for the values, add Decimal number formatters. The final window layout should look like Figure 16.


Figure 16: Final window layout

We are now going to setup the bindings using collection operators, just like we did in the non-Core Data application. Bind the total area value label to the Rectangles array controller. Set Controller Key to arrangedObjects and the Model Key Path to @sum.area. The result should look like Figure 17. Next, bind the total perimeter value field to the @sum.perimeter Model Key Path.


Figure 17: Total area bindings

We are now finished with our changes. You should be able to run the application and have the area and perimeter user interface elements work appropriately. Our application is nearly identical to our non-Core Data application, and all we had to do was write four methods for the area and perimeter. And this includes support for undo! Not to shabby.

Caveat Emptor

There is one subtle difference from our non-Core Data application. In our previous application, we store an array of rectangles in the document. This means that as we added rectangles, their order is preserved. Core Data uses sets, instead of arrays, which means they are unordered. But surely they have to show up in some order in the table. Unordered just means the order is not consistent. The list of rectangles may show up in a different order every time you load the file. Since the user can sort the table by clicking on one of the table columns, the easy way to fix the problem is to always sort the table by a column, by default. To truly remember the order of the items as they were entered is a very difficult Core Data problem, though. And even though setting up default column sorting is significantly easier, it is still beyond the scope of this article. For now, we'll just have to deal with that limitation.

Also, this article just scratches the tip of the Core Data iceberg. There are many more advanced topics, such a relationship properties, threading techniques, and migration between different versions of your model. Apple's documentation has a lot of information on these topics.

Conclusion

Even though Core Data is a complicated subject with some limitations, it is still a hugely valuable asset to Mac OS X development. It can save you from writing a lot of your own model code, just as Cocoa bindings can save you from writing a lot of controller code. It won't eliminate code completely, but I urge you to at least investigate Core Data for your future applications.


Dave Dribin has been writing professional software for over eleven years. After five years programming embedded C in the telecom industry and a brief stint riding the Internet bubble, he decided to venture out on his own. Since 2001, he has been providing independent consulting services, and in 2006, he founded Bit Maki, Inc. Find out more at http://www.bitmaki.com/ and http://www.dribin.org/dave/.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasn’t sued them for copyright infringement, but... | Read more »
Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »
Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »

Price Scanner via MacPrices.net

13-inch M2 MacBook Airs in stock today at App...
Apple has 13″ M2 MacBook Airs available for only $849 today in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty is included,... Read more
New today at Apple: Series 9 Watches availabl...
Apple is now offering Certified Refurbished Apple Watch Series 9 models on their online store for up to $80 off MSRP, starting at $339. Each Watch includes Apple’s standard one-year warranty, a new... Read more
The latest Apple iPhone deals from wireless c...
We’ve updated our iPhone Price Tracker with the latest carrier deals on Apple’s iPhone 15 family of smartphones as well as previous models including the iPhone 14, 13, 12, 11, and SE. Use our price... Read more
Boost Mobile will sell you an iPhone 11 for $...
Boost Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering an iPhone 11 for $149.99 when purchased with their $40 Unlimited service plan (12GB of premium data). No trade-in is required... Read more
Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobile’s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Apple’s one-year warranty is... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16″ and 14″ MacBook Pros along with 13″ and 15″ MacBook... Read more
Every model of Apple’s 13-inch M3 MacBook Air...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices are the lowest currently available for new 13″ M3 MacBook Airs among... Read more
Sunday Sale: Apple iPad Magic Keyboards for 1...
Walmart has Apple Magic Keyboards for 12.9″ iPad Pros, in Black, on sale for $150 off MSRP on their online store. Sale price for online orders only, in-store price may vary. Order online and choose... Read more

Jobs Board

Solutions Engineer - *Apple* - SHI (United...
**Job Summary** An Apple Solution Engineer's primary role is tosupport SHI customers in their efforts to select, deploy, and manage Apple operating systems and Read more
DMR Technician - *Apple* /iOS Systems - Haml...
…relevant point-of-need technology self-help aids are available as appropriate. ** Apple Systems Administration** **:** Develops solutions for supporting, deploying, Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.