Core Data iOS: Designing a Data Model and Building Data Objects

Exclusive offer: get 50% off this eBook here
Core Data iOS Essentials

Core Data iOS Essentials — Save 50%

A fast-paced, example-driven guide guide to data-drive iPhone, iPad, and iPod Touch applications

$26.99    $13.50
by B.M.Harwani | May 2011 | Web Services Web Graphics & Video

This article introduces the working of UITableView and explains step-by-step how information is displayed via the table view. Array can be used to display information through the Table View control. The article explains the different methods used in displaying information through table view and also how to add more information to the existing information being displayed via table view.

Assume that we want to create an application "Sales Record System of a Store" where we want to keep the information of the customers along with the product details sold to each of them.

In this article by B.M. Harwani, author of Core Data iOS Essentials, we will design a data model for keeping the customer's information, that is, we will define a Customer entity and its attributes. After designing data model, we build data object (classes) associated with the Customer entity.

 

Core Data iOS Essentials

Core Data iOS Essentials

A fast-paced, example-driven guide guide to data-drive iPhone, iPad, and iPod Touch applications

        Read more about this book      

(For more resources on this subject, see here.)

To design data model, we need to create a new project. So, let's start with.

Creating a new project

To create a new project, perform the following steps:

  1. Launch Xcode and create a new project by selecting the File | New Project option.
  2. The New Project Assistant window will appear, prompting us to select a template for the new project, as shown in the next screenshot. We will select the Navigation-based Application template.
  3. Ensure that the Use Core Data for storage checkbox is checked and click on the Choose... button.
  4. On selecting the Choose... button, we will get a dialog box to specify the name and the location of the project. Let us keep the location the same as default (Documents folder) and assign the project name as: prob (any name).
  5. Click on Save. Xcode will then generate the project files and the project gets opened in the Xcode project window.

The checkbox Use Core Data for storage will ask Xcode to provide all the default code that is required for using Core Data. This option is visible with only two project templates: Navigationbased Application and Window-based Application templates.

Designing the data model

Designing a data model means defining entities, attributes, and relationships for our application using a special tool. Xcode includes a data modeling tool (also known as Data Model Editor or simply a modeler) that facilitates the creation of entities, defining attributes, and the relationships among them.

Data Model Editor

The Data Model Editor is a data modeling tool provided by Xcode that makes the job of designing a data model quite easy. It displays the browser as well as a diagram view of the data model. The Browser view displays two panes, the Entity pane and the Properties pane, for defining entities and their respective properties. The diagram view displays rounded rectangles that designate entities and lines to show relationships among the entities.

Adding an entity

To add an entity to our data model, perform the following steps:

  1. Invoke the data modeling tool by double-clicking the prob.xcdatamodel file in the Resources group found in the Xcode Project window.
  2. Xcode's data modeling tool will open and we will find that an entity by default is already created for us by the name: Event (as shown in the next image) with an attribute: timeStamp.

  3. We can delete or rename the default entity Event as desired. Let us select the default Event entity and delete it by clicking on the minus (-) button in the Entity pane followed by either choosing plus (+) button in the Entity pane or by choosing Design | Data Model | Add Entity option from the menu bar. This will add a blank entity (by the name Entity) to our data model, which we can rename as per our requirements. Let us set the name of the new entity as: Customer.

  4. Automatically, an instance of NSManagedObject will be created to represent our newly created Customer entity. The next step is to add attributes to this entity.

Adding an attribute property

We want to add three attributes by name—name, emailid, and contactno—to the Customer entity. Let's follow the steps mentioned next for the same:

  1. Select the entity and choose the Design | Data Model | Add Attribute option from the menu bar or select the + (plus) button in the Property pane. A menu with several options such as Add Attribute, Add Fetched property, Add Relationship, and Add Fetch Request will pop up.
  2. We select the Add Attribute option from the popped up menu. We see that a new attribute property is created for our Customer entity by a default name: newAttribute in the inspector.
  3. Let us rename our new attribute as: name (as we will be using this attribute to store the names of the customers).
  4. Then, we set the type of the name attribute to String as shown in the next screenshot (as names consists of strings):

  5. Below the Name field are three checkboxes: Optional, Transient, and Indexed. Though we will be using the Optional checkbox for the name attribute, let us see the usage of all three:
    • Optional: If this checkbox is checked, it means the entity can be saved even if the attribute is nil (empty). If this checkbox is unchecked and we try to save the entity with this attribute set to nil, it will result in a validation error. When used with a relationship, if the checkbox is checked it means that the relationship can be empty. Suppose that we create one more entity say: Credit Card (where information of the customer's credit card is kept). In that case, the relationship from customer to the credit card will be optional (we have to leave this checkbox checked) as a customer may or may not have a credit card. And if we create an entity say: Product—in that case, the relationship from the Customer to the Product cannot be empty as a customer will definitely buy at least a single product (the checkbox has to be unchecked).
    • Transient: This checkbox, if checked, means that the attribute or the relationship is of a temporary nature and we don't want it to be stored (persist) in the persistent store. This checkbox must be unchecked for the attributes or relationship that we want to persist (to be stored on the disk).
    • Indexed: This checkbox has to be checked to apply indexing on the attribute. It is used when we want to perform sorting or searching on some attribute. By checking this checkbox, an index will be created on that attribute and the database will be ordered on that attribute.

Types of attributes

Using the Type drop-down list control, we select the data type (that is, numerical, string, date, and so on) of the attribute to specify the kind of information that can be stored in the attribute. The following is the list of data types:

  • Integer 16, Integer 32, and Integer 64 data types are for storing signed integers. The range of values that these types are able to store is as follows:
    • Integer 16:-32,768 to 32, 767
    • Integer 32:-2,147,483,648 to 2,147,483,647
    • Integer 64:-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
  • Decimal, Double, and Float data types are for storing fractional numbers. The Double data type uses 64 bits to store a value while the Float data type uses 32 bits for storing a value. The only limitation with these two data types is that they round off the values. To avoid any rounding of values, the Decimal data type is preferred. The Decimal type uses fixed point numbers for storing values, so the numerical value stored in it is not rounded off.
  • String data type is used for storing text contents.
  • Boolean data type is used for storing YES or NO values.
  • Date data type is used for storing dates as well as timestamps.
  • Binary data type is used for storing binary data.
  • Transformable data type works along with Value Transformers that help us create attributes based on any Objective-C class, that is, we can create custom data types other than the standard data types. This data type can be used to store an instance of UIColor, UIImage, and so on. It archives objects to instances of NSData.

Below the Type drop-down menu, we will see a few more fields in the detail pane, as shown in the next screenshot:

Fields applying constraints

Min Length: and Max Length: fields are for applying constraints of minimum and maximum number of characters to an attribute. If we exceed the range supplied in these fields, we get a validation error. Meaning, if we enter the string of fewer characters than the value supplied in Min Length: or the string has more characters than the value supplied in Max Length: field, this will result in a validation error while saving managed objects.

Reg. Ex: field stands for regular expression and is used for applying validation checks on the data entered in the attribute by making use of regular expressions.

Default Value: field is for specifying default value of the attribute. If we create a new managed object, the attribute will automatically be set to the default value specified in this field.

Let us add two more attributes to the Customer entity: emailid and contactno (for storing a customer's e-mail address and contact number, respectively). These two attributes will also be of type: String as shown in the next screenshot. Now, save the .xcdatamodel.

 

Core Data iOS Essentials A fast-paced, example-driven guide guide to data-drive iPhone, iPad, and iPod Touch applications
Published: April 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on this subject, see here.)

Building data objects for the Customer entity

To build the data objects (classes) for the entity defined in the data model, perform the following steps:

  1. Click on the entity in Data Model Editor and then go to File | New File.
  2. We get a dialog box to choose a template for the new file. Select Cocoa Touch Class from under the iOS heading in the left pane and select the Managed Object Class template, as shown in the next screenshot followed by Next button. This template is visible only when the editing pane is currently showing a Core Data model and is the active pane.

    Core Data iOS tutorial

  3. We get a dialog box that prompts for the location of generating managed object class, as shown in the following screenshot. The name of the subclass is based on the Entity name, so we will not be prompted to provide name for the subclass. Keeping the values as default, let's click on the Next button.

    Core Data iOS tutorial

  4. Check the Customer entity in the dialog box that appears, as shown in the next screenshot (a subclass of NSManagedObject will be created for our Customer entity).
  5. We find two checkboxes that are already checked: Generate accessors and Generate Obj-C 2.0 Properties (these checkboxes will create properties for all the attributes in the new class). We also find one unchecked checkbox: Generate validation methods, which if checked, will generate method stubs for validating the attributes of our entity (but we leave it unchecked for the time being). Keeping only the two checkboxes checked, click on Finish.

Core Data iOS tutorial

We find two files: Customer.h and Customer.m generated for our entity in the Classes folder of Xcode Project window. The code in header file, Customer.h, will appear as shown next:

// Customer.h
// prob

#import <CoreData/CoreData.h>
@interface Customer : NSManagedObject
{
}

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSString * emailid;
@property (nonatomic, retain) NSString * contactno;

@end

We can see that three properties are defined, name, emailid, and contactno, one for each attribute that we defined in the model editor. These properties are not declared in the header, as they will be created dynamically at runtime.

The code in the implementation file, Customer.m, will appear as shown next:

// Customer.m
// prob

#import "Customer.h"

@implementation Customer

@dynamic name;
@dynamic emailid;
@dynamic contactno;

@end

In the implementation file, we find that the properties are marked as dynamic to inform the compiler not to generate accessors and mutators for the property and will be provided (generated) by the super class at runtime and hence, not to display any warning message related to them. The getter/setter implementations that Core Data provides are KVO compliant.

Understanding code of autogenerated files

On creating a new application through Xcode, it simplifies the task of the developer by generating several files for us that are required for successful execution of the application. The two important files that are autogenerated by Xcode are application delegate files. The question is what is the usage of application delegate files? The answer is quite simple: there are several important events that happen in the life of an application. The two most important events are launching and termination of the application. The application needs to know when these events happen or are about to happen. The iPhone OS notifies about these events through Application Delegate by calling its appropriate methods. iPhone OS calls the applicationDidFinishLaunching method when it finishes the launch procedure and calls applicationWillTerminate when the application is terminated, so as to close any open files.

Let's have a look at the code of the autogenerated files. We begin with the header file of Application Delegate:

Header file of Application Delegate

The header file of Application Delegate, progAppDelegate.h contains some default code, as shown in the following code listing:

// probAppDelegate.h
// prob

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>

@interface probAppDelegate : NSObject <UIApplicationDelegate> {

UIWindow *window;
UINavigationController *navigationController;

@private
NSManagedObjectContext *managedObjectContext_;
NSManagedObjectModel *managedObjectModel_;
NSPersistentStoreCoordinator *persistentStoreCoordinator_;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController
*navigationController;

@property (nonatomic, retain, readonly) NSManagedObjectContext
*managedObjectContext;
@property (nonatomic, retain, readonly) NSManagedObjectModel
*managedObjectModel;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator
*persistentStoreCoordinator;

- (NSURL *)applicationDocumentsDirectory;
- (void)saveContext;

@end

The preceding file declares instance variables of the NSManagedObjectModel, NSManagedObjectContext, and the NSPersistentStoreCoordinator class. Also, all of the variables including an NSString instance are defined as properties with the two attributes: retain and nonatomic. The retain attribute informs the compiler to retain (keep) the instance variable and not to flush from memory while being used. The nonatomic attribute informs the compiler that when it will be asked to generate the accessor and mutator methods of the outlets (synthesized), it should generate them without any additional code of implementing multithreading. The nonatomic attribute is used for simple applications.

Let us also see the autogenerated code in Application Delegate's implementation file: probAppDelegate.m.

Core Data iOS Essentials A fast-paced, example-driven guide guide to data-drive iPhone, iPad, and iPod Touch applications
Published: April 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on this subject, see here.)

Implementation file of Application Delegate

The implementation file of Application Delegate, progAppDelegate.m, contains some default code, as shown in the following code listing:

// probAppDelegate.m
// prob

#import "probAppDelegate.h"
#import "RootViewController.h"

@implementation probAppDelegate

@synthesize window;
@synthesize navigationController;

- (void)awakeFromNib {
RootViewController *rootViewController = (RootViewController *)
[navigationController topViewController];
rootViewController.managedObjectContext = self.
managedObjectContext;
}

- (BOOL)application:(UIApplication *)
application didFinishLaunchingWithOptions:(NSDictionary *)
launchOptions {
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
return YES;
}

In awakeFromNib method, the root view controller of the application is set equal to the top view controller of the navigation controller. In applicationDidFinishLaunching method, the view of the navigation controller is set as a subview of the content view and hence is displayed to the user.

- (void)applicationWillTerminate:(UIApplication *)application {
[self saveContext];
}

- (void)saveContext {

NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.
managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] &&
![managedObjectContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error
userInfo]);
abort();
}
}
}

applicationWillTerminate method

The applicationWillTerminate method is invoked just before exiting from the application and is usually used for saving the modifications applied to the managed object via managed object context. That is, the managed object context (containing the modifications applied to the managed object) is saved to the persistent store. The save action method is used for saving the managed object context to the persistent store, that is, the changes made to the managed object context are committed through this method. An error will be displayed if there occurs some problem while saving the managed object context.

- (NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext_ != nil) {
return managedObjectContext_;
}
NSPersistentStoreCoordinator *coordinator =
[self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext_ = [[NSManagedObjectContext alloc] init];
[managedObjectContext_ setPersistentStoreCoordinator:coordina
tor];
}
return managedObjectContext_;
}

managedObjectContext method

Every application has at least one managed object context. The managed object context maintains the state of the managed object after it is loaded in memory. All modifications that we apply to the managed object are actually applied to the managed object context. It keeps track of all the changes made to the managed object since the last time it was loaded in memory and hence helps in undoing any changes made to the managed object (if required). When we want to commit the modifications made to the managed object, we save the managed object context to the persistent store.

The managed contexts are not thread-safe, which means there are many chances of faults when managed object contexts are shared between threads or are accessed from multiple threads simultaneously. To avoid any conflicts, we need to apply the Locking mechanism. In other words, contexts in general should not be shared between threads. Apple's advice is to use one context per thread and to merge changes.

In the preceding method, a check is made to see if the instance variable, managedObjectContext, already exists or not (nil value means the instance variable does not exist). If the instance variable exists, it is returned, else it is created. In order to deal with persistent store, the managed object context needs a reference to a PersistentStoreCoordinator. Recall that the PersistentStoreCoordinator is the essential middle layer in the stack that helps in storing and retrieving the managed object model from the persistent store (in the form of managed object context). So, to create the managedObjectContext instance variable, we check for a pointer to the NSPersistentStoreCoordinator. If the pointer to PersistentStoreCoordinator exists, we create a new managedObjectContext and after linking it with the pointer to the PersistentStoreCoordinator, return it.

- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel_ != nil) {
return managedObjectModel_;
}
NSString *modelPath = [[NSBundle mainBundle]
pathForResource:@"prob"
ofType:@"momd"];
NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
managedObjectModel_ = [[NSManagedObjectModel alloc]
initWithContentsOfURL:modelURL];
return managedObjectModel_;
}

managedObjectModel method

This method first checks if the instance variable managedObjectModel (of NSManagedObjectModel class) already exists or not (nil value means the instance variable does not exist). If the instance variable exists, it is returned, or else it is created by merging all the data models found in the application bundle. It also means that we can have several data models (created in separate .xcdatamodelfiles) in an application. All the data models (entities and their relationships) contained in different files will be merged (combined) into a single managed object model.

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}
NSURL *storeURL = [[self applicationDocumentsDirectory]
URLByAppendingPathComponent:@"customersdata.sqlite"];
NSError *error = nil;
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator
alloc]
initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQL
iteStoreType configuration:nil URL:storeURL options:nil error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator_;
}

persistentStoreCoordinator method

The NSPersistentStoreCoordinator class is meant for storing and retrieving the managed object model from the persistent store. It also helps in avoiding redundancy if multiple calls are made by different classes on the same file at the same time. The multiple calls are serialized by the NSPersistentStoreCoordinator class to avoid redundancy.

The preceding code first checks whether the instance variable, persistentStoreCoordinator, exists or not. If it exists, it is returned, or else it is created. Because we want to store the names of the customers in the file: customersdata.sqlite, we define a path to the file in the Documents directory of our application's sandbox.

We want the format of the persistent store to be of SQLite type, hence the parameter NSSQLiteStoreType is passed to the method to specify the type of the persistent store. NSSQLiteStoreType is a constant that tells Core Data to use a SQLite database for its persistent store. If we want to store the information in binary format, we may use the constant, NSBinaryStoreType for the persistent store. If anything goes wrong while creating the instance variable persistentStoreCoordinator, an error will be displayed:

- (NSURL *)applicationDocumentsDirectory {
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumen
tDirectory inDomains:NSUserDomainMask] lastObject];
}

applicationDocumentsDirectory method

This method is for finding the location to store the persistent store file. In Xcode, each application has its own sandboxed Documents directory designed for the storage of files. So, we retrieve a list of the cache directories from the SearchPathForDirectoriesInDomains method and write code to find the Documents folder specific to our application.

- (void)dealloc {
[managedObjectContext_ release];
[managedObjectModel_ release];
[persistentStoreCoordinator_ release];
[navigationController release];
[window release];
[super dealloc];
}

@end

The dealloc method is for releasing the memory assigned to different instance variables.

As seen in the previous code we specify the file: customersdata.sqlite for storing the names of the customers in the NSURL statement of the persistentStoreCordinator method.

Summary

In this article, we saw how the Xcode's Data Model Editor can be used for creating Entity, defining its attributes and how its Data Model (class) associated with the Entity can be automatically generated. We also had a brief idea of different fields that appear while defining attributes of an Entity.


Further resources on this subject:


About the Author :


B.M.Harwani

B.M.Harwani is the founder and owner of Microchip Computer Education (MCE), based in Ajmer, India that provides computer education in all programming and web developing platforms. He graduated with a BE in computer engineering from the University of Pune, and also has a 'C' Level (master's diploma in computer technology) from DOEACC, Government Of India. Being involved in the teaching field for over 16 years, he has developed the art of explaining even the most complicated topics in a straightforward and easily understandable fashion. He has written several books on various subjects that includes JSP, JSF, EJB, PHP, .Net, Joomla, jQuery, and Smartphones. He also writes articles on a variety of computer subjects, which can be seen on a number of websites. To know more, visit his blog, http://bmharwani.com/blog.

The list of books written by B.M.Harwani are Programming & Problem Solving through C (BPB, 2004), Learn Tally in Just Three Weeks (Pragya, 2005), Data Structures and Algorithms through C (CBC, 2006), Master Unix Shell Programming (CBC, 2006), Business Systems (CBC, 2006), Practical Java Projects (Shroff, 2007), Practical Web Services (Shroff, 2007), Java for Professionals (Shroff, 2008), C++ for Beginners (Shroff, 2009), Practical ASP.NET 3.5 Projects (Shroff, 2009), Java Server Faces—A Practical Approach for Beginners (PHI Learning, 2009), Practical JSF Project using NetBeans (PHI Learning, 2009), Foundation Joomla (Friends of ED, 2009), Practical EJB Projects (Shroff, 2009), Data Structures and Algorithms in C++ (Dreamtech Press, 2010), Developing Web Applications in PHP and AJAX (Tata McGraw Hill, 2010), and jQuery Recipes (Apress, 2010).

Books From Packt


iPhone Applications Tune-Up
iPhone Applications Tune-Up

Cocos2d for iPhone 0.99 Beginner's Guide
Cocos2d for iPhone 0.99 Beginner's Guide

iPhone JavaScript Cookbook
iPhone JavaScript Cookbook

iPhone User Interface Cookbook: RAW
iPhone User Interface Cookbook: RAW

Android User Interface Development: Beginner's Guide
Android User Interface Development: Beginner's Guide

Flash Development for Android Cookbook
Flash Development for Android Cookbook

Funambol Mobile Open Source
Funambol Mobile Open Source

MeeGo 1.0 Mobile Application Development Cookbook
MeeGo 1.0 Mobile Application Development Cookbook


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
h
2
u
N
j
t
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software