An iOS 5 iPad Core Data Tutorial

From Techotopia
Revision as of 18:05, 1 February 2016 by Neil (Talk | contribs) (Text replacement - "<google>ADSDAQBOX_FLOW</google>" to "<htmlet>adsdaqbox_flow</htmlet>")

Jump to: navigation, search
PreviousTable of ContentsNext
Working with iOS 5 iPad Databases using Core DataAn Overview of iOS 5 iPad Multitouch, Taps and Gestures


<google>BUY_IOS5_IPAD</google>


In the previous chapter, entitled Working with iOS 5 iPad Databases using Core Data, an overview of the Core Data stack was provided, together with details of how to write code to implement data persistence using this infrastructure. In this chapter we will continue to look at Core Data in the form of a step by step tutorial that implements data persistence using Core Data in an iOS 5 iPad application.


Contents


The iPad Core Data Example Application

The application developed in this chapter will take the form of the same contact database application used in previous chapters, the objective being to allow the user to enter name, address and phone number information into a database and then search for specific contacts based on the contact’s name.

Creating a Core Data based iPad Application

As is often the case, we can rely on Xcode to do much of the preparatory work for us when developing an iPad application that will use Core Data. Currently, however, only the Master-Detail Application, Utility Application and Empty Application project templates offer the option to automatically include support for Core Data.

To create the example application project, launch Xcode and select the option to create a new project. In the new project window, select the Empty Application option. In the next screen make sure that the Device Family menu is set to iPad and that the check box next to Use Core Data is selected. In the Product Name and Class Prefix fields enter coreData and click Next to select a location to store the project files.

Xcode will create the new project and display the main project window. In addition to the usual files that are present when creating a new project, this time an additional file named coreData.xcdatamodeld is also created. This is the file where the entity descriptions for our data model are going to be stored.


Creating the Entity Description

The entity description defines the model for our data, much in the way a schema defines the model of a database table. To create the entity for the Core Data application, select the coreData.xcdatamodeld file to load the entity editor:


The Xcode data model screen

Figure 41-1


To create a new entity, click on the Add Entity button located in the bottom panel. In the text field that appears beneath the Entities heading name the entity Contacts. With the entity created, the next step is to add some attributes that represent the data that is to be stored. To do so, click on the Add Attribute button.

In the Attribute pane, name the attribute name and set the Type to String. Repeat these steps to add two other String attributes named address and phone respectively:


Designing the Core Data Model for an iPad iOS 5 app

Figure 41-2


The entity is now defined and it is time to start writing code.

Adding a View Controller

In order to automatically add Core Data support to our application we had to choose the Empty Application project template option when we started Xcode. As such, we now need to create our own view controller.

Within the Xcode project navigator panel, Ctrl-click on the coreData folder entry. From the popup menu, select New File... In the new file panel, select the iOS Cocoa Touch Class category followed by the Objective-C class icon and click Next. On the following options screen, make sure the Subclass of menu is set to UIViewController and name the class coreDataViewController. Select the With XIB for user interface check box and make sure the Targeted for iPad option is set. Finally, make sure the Subclass of menu is set to UIViewController and name the class coreDataViewController before clicking on the Next button. Click Next and on the final panel click on Create.

Now that we have added the view controller class to the application we need to modify our app delegate to make this the root view controller. In the Xcode project navigator, select the coreDataAppDelegate.h file and modify it to add a reference to our new view controller:

#import <UIKit/UIKit.h>

@class coreDataViewController;

@interface coreDataAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) IBOutlet coreDataViewController *viewController;
@property (strong, nonatomic) UIWindow *window;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
@end

With an instance of the view controller declared in the interface file we now need to modify the applicationDidFinishLaunchingWithOptions method located in the coreDataAppDelegate.m implementation file to initialize and allocate the coreDataViewController instance and assign it as the window view so that it is visible to the user. Note also that we have to import coreDataViewController.h into this file and synthesize accessors for the viewController object:

#import "coreDataAppDelegate.h"
#import "coreDataViewController.h"

@implementation coreDataAppDelegate

@synthesize window = _window;
@synthesize managedObjectContext = __managedObjectContext;
@synthesize managedObjectModel = __managedObjectModel;
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
@synthesize viewController;

- (BOOL)application:(UIApplication *)application 
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] 
      initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    viewController = [[coreDataViewController alloc] 
      initWithNibName:@"coreDataViewController" bundle:nil];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window addSubview:viewController.view];
    [self.window makeKeyAndVisible];
    return YES;
}
.
.
.
@end

Adding Actions and Outlets to the View Controller

As with previous instances of our Contacts application, our Core Data example will include a user interface that needs to accept and display name, address and phone information and to react to the selection of the save and find buttons. In addition, a status label will be used to provide the user with feedback. To declare the outlets and actions associated with these user interface components, select the coreDataViewController.h file and modify the class declaration so that it reads as follows:

#import <UIKit/UIKit.h>

@interface coreDataViewController : UIViewController 

@property (strong, nonatomic) IBOutlet UITextField *name;
@property (strong, nonatomic) IBOutlet UITextField *address;
@property (strong, nonatomic) IBOutlet UITextField *phone;
@property (strong, nonatomic) IBOutlet UILabel *status;
- (IBAction) saveData;
- (IBAction) findContact;
@end

Next, select the coreDataViewController.m implementation file, add an @synthesize directive for the outlets, import the coreDataAppDelegate.h file and add template methods for the two declared actions:

#import "coreDataViewController.h"
#import "coreDataAppDelegate.h"

@interface coreDataViewController ()

@end

@implementation coreDataViewController
@synthesize name, address, phone, status;

- (void) saveData
{
}

- (void) findContact
{
}
.
.
@end

Designing the User Interface

With the actions and outlets defined, now is a good time to design the user interface and establish the connections so select the coreDataViewController.xib file to begin the design work. The user interface and corresponding connections used in this tutorial are the same as those in previous data persistence chapters. The completed view should, once again, appear as outlined in Figure 41-3:


The user interface of an iPad iOS 5 Core Data example app

Figure 41-3


Before proceeding, stretch the status label (located above the two buttons) so that it covers most of the width of the view. Finally, edit the label and remove the word “Label” so that it is blank.

Next, connect the three text fields and status label to the name, address, phone and status outlets respectively by holding down the Ctrl key and clicking and dragging from File’s Owner icon to the corresponding component. From the resulting menu select the outlet corresponding to the selected view object.

The last step involves connecting the two buttons to the corresponding actions. First, display the Connections Inspector (View -> Utilities -> Show Connections Inspector) then select the Save button. Click inside the small circle next to the Touch Up Inside event in the Connections Inspector panel and drag the blue line to the File’s Owner object. To establish the connection, select saveData from the resulting menu. Repeat these steps to connect the Find button to the findContact action.

Saving Data to the Persistent Store using Core Data

When the user touches the Save button the saveData method is called. It is within this method, therefore, that we must implement the code to obtain the managed object context and create and store managed objects containing the data entered by the user. Select the coreDataViewController.m file, scroll down to the template saveData method and implement the code as follows:

- (void) saveData
{
   coreDataAppDelegate *appDelegate = 
    [[UIApplication sharedApplication] delegate];

   NSManagedObjectContext *context = 
    [appDelegate managedObjectContext];
   NSManagedObject *newContact;
   newContact = [NSEntityDescription
       insertNewObjectForEntityForName:@"Contacts"
       inManagedObjectContext:context];
   [newContact setValue:name.text forKey:@"name"];
   [newContact setValue:address.text forKey:@"address"];
   [newContact setValue:phone.text forKey:@"phone"];
   self.name.text = @"";
   self.address.text = @"";
   self.phone.text = @"";
   NSError *error;
   [context save:&error];
   self.status.text = @”Contact saved”;
}

The above code identifies the application delegate instance and uses that object to identify the managed object context. This context is then used to create a new managed object using the Contacts entity description. The setValue method of the managed object is then called to set the name, address and phone attribute values of the managed object (which in turn are read from the text field user interface components). Finally the context is instructed to save the changes to the persistent store with a call to the context’s save method.

Retrieving Data from the Persistent Store using Core Data

In order to allow the user to search for a contact it is now necessary to implement the findContact action method. As with the save method, this method will need to identify the application delegate and managed object context. It will then need to obtain the entity description for the Contacts entity and then create a predicate to ensure that only objects with the name specified by the user are retrieved from the store. Matching objects are placed in an array from which the attributes for the first match are retrieved using the valueForKey method and displayed to the user. A full count of the matches is displayed in the status field.

The code to perform these tasks is as follows:

- (void) findContact
{
    coreDataAppDelegate *appDelegate = 
       [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *context = 
       [appDelegate managedObjectContext];
    NSEntityDescription *entityDesc = 
       [NSEntityDescription entityForName:@"Contacts" 
       inManagedObjectContext:context];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDesc];
    NSPredicate *pred = 
       [NSPredicate predicateWithFormat:@"(name = %@)", 
       self.name.text];
    [request setPredicate:pred];
    NSManagedObject *matches = nil;
    NSError *error;
    NSArray *objects = [context executeFetchRequest:request 
        error:&error];
    if ([objects count] == 0) {
       self.status.text = @"No matches";
    } else {
       matches = [objects objectAtIndex:0];
       self.address.text = [matches valueForKey:@"address"];
       self.phone.text = [matches valueForKey:@"phone"];
       self.status.text = [NSString stringWithFormat:
           @"%d matches found", [objects count]];
    }
}

Updating viewDidUnload

Prior to compiling and running the example Core Data iPad iOS application, it is important that we add code to help the system free up any resources that were allocated during execution of the application. To do so, edit the coreDataViewController.m file and modify the viewDidUnload method as follows:

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    self.name = nil;
    self.address = nil;
    self.phone = nil;
    self.status = nil;
}

Building and Running the Example Application

The final step is to build and run the application. Click on the Run button located in the toolbar of the main Xcode project window. If errors are reported check the syntax of the code you have written, using the error message provided by Xcode as guidance. Once the application compiles it will launch and load into the iOS Simulator. Enter some test contacts (some with the same name). Having entered some test data, enter the name of the contact for which you created duplicate records and click the Find button. As shown in Figure 41 4, the address and phone number of the first matching record should appear together with an indication in the status field of the total number of matching objects that were retrieved:


An iPad iOS 5 Core Data example app running

Figure 41-4


<google>BUY_IOS5_IPAD</google>



PreviousTable of ContentsNext
Working with iOS 5 iPad Databases using Core DataAn Overview of iOS 5 iPad Multitouch, Taps and Gestures