Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Beginning iOS5 Development.pdf
Скачиваний:
7
Добавлен:
09.05.2015
Размер:
15.6 Mб
Скачать

484

CHAPTER 13: Basic Data Persistence

default. Click it to deselect it. We don’t want this attribute to be optional—a line that doesn’t correspond to a label on our interface is useless.

Selecting the Transient checkbox creates a transient attribute, which is used to specify a value that is held by managed objects while the app is running, but never saved to the data store. Since we do want the line number saved to the data store, leave the Transient checkbox unchecked.

Selecting the Indexed checkbox will cause an index in the underlying SQL database to be created on the column that holds this attribute’s data. Leave the Indexed checkbox unchecked. Since the amount of data is small, and we won’t provide the user with a search capability, there’s no need for an index.

Beneath that are more settings, allowing you to do some simple data validation by specifying minimum and maximum values for the integer, a default value, and more. We won’t be using any of these settings in this example.

Now, make sure the Line entity is selected and click the plus sign to add a second attribute. Change the name of your new attribute to lineText and change its Type to String. This attribute will hold the actual data from the text field. Leave the Optional checkbox checked for this one; it is altogether possible that the user won’t enter a value for a given field.

NOTE: When you change the Type to String, you’ll notice that the inspector shows a slightly different set of options for setting a default value or limiting the length of the string. Although we

won’t be using any of those options for this application, it’s nice to know they’re there.

Guess what? Your data model is complete. That’s all there is to it. Core Data lets you point and click your way to an application data model. Let’s finish building the application so you can see how to use our data model from our code.

Creating the Persistence View and Controller

Because we selected the Empty Application template, we weren’t provided with a view controller. Go back to the project navigator, single-click the Core Data Persistence folder, and press N or select File New New File… to bring up the new file assistant. Select UIViewController subclass from the Cocoa Touch heading, and click Next. On the next sheet, name the class BIDViewController, select a Subclass of UIViewController, make sure the box labeled Targeted for iPad is unchecked, and check the box that says With XIB for user interface to have Xcode create a nib file automatically. Click Next, and choose the directory in which to save the file. When you’re finished,

BIDViewController.h, BIDViewController.m, and BIDViewController.xib will be placed in your Core Data Persistence folder.

www.it-ebooks.info

CHAPTER 13: Basic Data Persistence

485

Select BIDViewController.h, and make the following changes, which should look very familiar to you:

#import <UIKit/UIKit.h>

@interface BIDViewController : UIViewController

@property (weak, nonatomic) IBOutlet UITextField *line1; @property (weak, nonatomic) IBOutlet UITextField *line2; @property (weak, nonatomic) IBOutlet UITextField *line3; @property (weak, nonatomic) IBOutlet UITextField *line4;

@end

Save this file. Next, select BIDViewController.xib to edit the GUI in Interface Builder. Design the view, and connect the outlets by following the instructions in the “Designing the Persistence Application Viewsection earlier in this chapter. Once your design is complete, select the view, bring up the attributes inspector, and select Light Gray Color from the Background popup menu. You might also find it useful to refer back to Figure 13–3. Once you’ve created the view, save the nib file.

In BIDViewController.m, insert the following code at the top of the file:

#import "BIDViewController.h"

#import "BIDAppDelegate.h"

@implementation BIDViewController

@synthesize line1; @synthesize line2; @synthesize line3; @synthesize line4;

Then insert the following code into the existing viewDidLoad and viewDidUnload methods:

- (void)viewDidLoad { [super viewDidLoad];

// Do any additional setup after loading the view from its nib.

BIDAppDelegate *appDelegate =

[[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context = [appDelegate managedObjectContext]; NSEntityDescription *entityDescription = [NSEntityDescription

entityForName:@"Line"

inManagedObjectContext:context];

NSFetchRequest *request = [[NSFetchRequest alloc] init]; [request setEntity:entityDescription];

NSError *error;

NSArray *objects = [context executeFetchRequest:request error:&error]; if (objects == nil) {

NSLog(@"There was an error!");

// Do whatever error handling is appropriate

}

for (NSManagedObject *oneObject in objects) {

www.it-ebooks.info

486

CHAPTER 13: Basic Data Persistence

NSNumber *lineNum = [oneObject valueForKey:@"lineNum"];

NSString *lineText = [oneObject valueForKey:@"lineText"];

NSString *fieldName = [NSString stringWithFormat:@"line%d", [lineNum integerValue]];

UITextField *theField = [self valueForKey:fieldName]; theField.text = lineText;

}

UIApplication *app = [UIApplication sharedApplication]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification

object:app];

}

- (void)viewDidUnload { [super viewDidUnload];

//Release any retained subviews of the main view.

//e.g. self.myOutlet = nil;

self.line1 = nil; self.line2 = nil; self.line3 = nil; self.line4 = nil;

}

Then add the following new method down at the bottom, just before the @end marker:

- (void)applicationWillResignActive:(NSNotification *)notification { BIDAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context = [appDelegate managedObjectContext]; NSError *error;

for (int i = 1; i <= 4; i++) {

NSString *fieldName = [NSString stringWithFormat:@"line%d", i]; UITextField *theField = [self valueForKey:fieldName];

NSFetchRequest *request = [[NSFetchRequest alloc] init];

NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line"

inManagedObjectContext:context]; [request setEntity:entityDescription]; NSPredicate *pred = [NSPredicate

predicateWithFormat:@"(lineNum = %d)", i]; [request setPredicate:pred];

NSManagedObject *theLine = nil;

NSArray *objects = [context executeFetchRequest:request error:&error];

if (objects == nil) { NSLog(@"There was an error!");

// Do whatever error handling is appropriate

}

www.it-ebooks.info

CHAPTER 13: Basic Data Persistence

487

if ([objects count] > 0)

theLine = [objects objectAtIndex:0];

else

theLine = [NSEntityDescription insertNewObjectForEntityForName:@"Line"

inManagedObjectContext:context];

[theLine setValue:[NSNumber numberWithInt:i] forKey:@"lineNum"]; [theLine setValue:theField.text forKey:@"lineText"];

}

[context save:&error];

}

.

.

.

Now, let’s look at the viewDidLoad method, which needs to check if there is any existing data in the persistent store. If there is, it should load the data and populate the fields with it. The first thing we do in that method is to get a reference to our application delegate, which we then use to get the managed object context that was created for us.

BIDAppDelegate *appDelegate = UIApplication sharedApplication] delegate];

NSManagedObjectContext *context = [appDelegate managedObjectContext];

Next, we create an entity description that describes our entity.

NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line"

inManagedObjectContext:context];

The next order of business is to create a fetch request and pass it the entity description so it knows which type of objects to retrieve.

NSFetchRequest *request = [[NSFetchRequest alloc] init]; [request setEntity:entityDescription];

Since we want to retrieve all Line objects in the persistent store, we do not create a predicate. By executing a request without a predicate, we’re telling the context to give us every Line object in the store.

NSError *error;

NSArray *objects = [context executeFetchRequest:request error:&error];

We make sure we got back a valid array, and log it if we didn’t.

if (objects == nil) { NSLog(@"There was an error!");

// Do whatever error handling is appropriate

}

Next, we use fast enumeration to loop through the array of retrieved managed objects, pull the lineNum and lineText values from it, and use that information to update one of the text fields on our user interface.

for (NSManagedObject *oneObject in objects) {

NSNumber *lineNum = [oneObject valueForKey:@"lineNum"]; NSString *lineText = [oneObject valueForKey:@"lineText"];

www.it-ebooks.info

488

CHAPTER 13: Basic Data Persistence

NSString *fieldName = [NSString stringWithFormat:@"line%@", lineNum];

UITextField *theField = [self valueForKey:fieldName]; theField.text = lineText;

}

Then, just as with all the other applications in this chapter, we register to be notified when the application is about to move out of the active state (either being shuffled to the background or exited completely), so we can save any changes the user has made to the data.

UIApplication *app = [UIApplication sharedApplication]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:)

name:UIApplicationWillResignActiveNotification

object:app]; [super viewDidLoad];

Let’s look at applicationWillResignActive: next. We start out the same way as the previous method, by getting a reference to the application delegate and using that to get a pointer to our application’s default context.

BIDAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *context = [appDelegate managedObjectContext];

After that, we go into a loop that executes four times, one time for each label.

for (int i = 1; i <= 4; i++) {

We construct the name of one of the four fields by appending i to the word line and use that to get a reference to the correct field using valueForKey:.

NSString *fieldName = [NSString stringWithFormat:@"line%d", i];

UITextField *theField = [self valueForKey:fieldName];

Next, we create our fetch request:

NSFetchRequest *request = [[NSFetchRequest alloc] init];

After that, we create an entity description that describes the Line entity we designed earlier in the data model editor and that uses the context we retrieved from the application delegate. Once we create the description, we feed it to the fetch request, so the request knows which type of entity to look for.

NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line"

inManagedObjectContext:context]; [request setEntity:entityDescription];

Next, we need to find out if there’s already a managed object in the persistent store that corresponds to this field, so we create a predicate that identifies the correct object for the field.

NSPredicate *pred = [NSPredicate predicateWithFormat:@"(lineNum = %d)", i];

[request setPredicate:pred];

www.it-ebooks.info

CHAPTER 13: Basic Data Persistence

489

After that, we declare a pointer to an NSManagedObject and set it to nil. We do this because we don’t know yet if we’re going to load a managed object from the persistent store or create a new one. We also declare an NSError that the system will use to notify us of the specific nature of the problem if we get back a nil array.

NSManagedObject *theLine = nil;

NSError *error;

Next, we execute the fetch request against the context.

NSArray *objects = [context executeFetchRequest:request error:&error];

Then we check to make sure that objects is not nil. If it is nil, then there was an error, and we should do whatever error checking is appropriate for our application. For this simple application, we’re just logging the error and moving on.

if (objects == nil) { NSLog(@"There was an error!");

// Do whatever error handling is appropriate

}

After that, we check if an object that matched our criteria was returned. If there is one, we load it. If there isn’t one, we create a new managed object to hold this field’s text.

if ([objects count] > 0)

theLine = [objects objectAtIndex:0];

else

theLine = [NSEntityDescription insertNewObjectForEntityForName:@"Line"

inManagedObjectContext:context];

Then we use key-value coding to set the line number and text for this managed object.

[theLine setValue:[NSNumber numberWithInt:i] forKey:@"lineNum"]; [theLine setValue:theField.text forKey:@"lineText"];

}

Finally, once we’re finished looping, we tell the context to save its changes.

[context save:&error];

}

Making the Persistence View Controller the Application’s Root

Controller

Because we used the Empty Application template instead of the Single View Application template, we have one more step to take before our fancy new Core Data application will work. We need to create an instance of BIDViewController to act as our application’s root controller and add its view as a subview of our application’s main window. Let’s do that now.

First, the application delegate needs a property to point to our view controller. Select BIDAppDelegate.h, and make the following changes to declare that property:

www.it-ebooks.info

490CHAPTER 13: Basic Data Persistence

#import <UIKit/UIKit.h>

@class BIDViewController;

@interface BIDAppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) IBOutlet UIWindow *window;

@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;

@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;

@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

@property (strong, nonatomic) BIDViewController *rootController;

-(void)saveContext;

-(NSURL *)applicationDocumentsDirectory;

@end

To make the root controller’s view a subview of the application’s window so that the user can interact with it, switch to BIDAppDelegate.m, and make the following changes at the top of that file:

#import "BIDAppDelegate.h"

#import "BIDViewController.h"

@implementation BIDAppDelegate

@synthesize window = _window;

@synthesize managedObjectContext = __managedObjectContext; @synthesize managedObjectModel = __managedObjectModel;

@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;

@synthesize rootController;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.rootController = [[BIDViewController alloc]

initWithNibName:@"BIDViewController" bundle:nil];

UIView *rootView = self.rootController.view; CGRect viewFrame = rootView.frame; viewFrame.origin.y += [UIApplication

sharedApplication].statusBarFrame.size.height; rootView.frame = viewFrame;

[self.window addSubview:rootView]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible];

return YES;

}

.

.

.

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]