186 ❘ CHAPTER BUILDING A CORE DATA APPLICATION LISTING 7-4 (continued) Task* managedTaskObject; NSManagedObjectContext *managedObjectContext; } @property (nonatomic, retain) Task* managedTaskObject; @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @end In the implementation fi le, you fi rst need to synthesize the properties that you declared in the header: @synthesize managedTaskObject,managedObjectContext; As in the EditTextController, you should implement the viewDidUnload method to set the properties defi ned in the class to nil: - (void)viewDidUnload { self.managedObjectContext=nil; self.managedTaskObject = nil; [super viewDidUnload]; } EditPriorityController.m Leave the numberOfSectionsInTableView method with the default implementation because the table will have only one section Change tableView:numberOfRowsInSection: to return four rows, one for each priority level Next, implement the tableView:cellForRowAtIndexPath: method to show the priority options in the appropriate cell: // Customize the appearance of table view cells - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @”Cell”; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } CH007.indd 186 9/18/10 9:48:45 AM Building the Editing Controllers // Set up the cell switch (indexPath.row) { case 0: cell.textLabel.text break; case 1: cell.textLabel.text break; case 2: cell.textLabel.text break; case 3: cell.textLabel.text break; default: break; } ❘ 187 = @”None”; = @”Low”; = @”Medium”; = @”High”; // place the checkmark next to the existing priority if (indexPath.row == [managedTaskObject.priority intValue] ) { cell.accessoryType=UITableViewCellAccessoryCheckmark; } return cell; } EditPriorityController.m This method should be familiar to you The fi rst few lines try to dequeue a cell as usual Then, the code determines the text of the cell based on which cell you are providing The last bit of code displays a checkmark next to the currently chosen priority for the task When a user taps a row, you need to save that selection in the Task object You will that in the tableView:didSelectRowAtIndexPath: method: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Deselect the currently selected row according to the HIG [tableView deselectRowAtIndexPath:indexPath animated:NO]; // Configure the managed object managedTaskObject.priority=[NSNumber numberWithInt:indexPath.row]; // Save the context NSError *error = nil; if (![self.managedObjectContext save:&error]) { // There was an error validating the date CH007.indd 187 9/18/10 9:48:46 AM 188 ❘ CHAPTER BUILDING A CORE DATA APPLICATION // Display error information NSLog(@”Unresolved error %@, %@”, error, [error userInfo]); UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@”Invalid Due Date” message:[[error userInfo] valueForKey:@”ErrorString”] delegate:nil cancelButtonTitle:@”OK” otherButtonTitles:nil ]; [alert show]; [alert release]; // Roll back the context to // revert back to the old priority [self.managedObjectContext rollback]; } else { // pop the view [self.navigationController popViewControllerAnimated:YES]; } } EditPriorityController.m The fi rst thing that this method does is deselect the selected row as I explained in the last section The next line sets the Task object’s priority field to the priority level selected on the screen Then, the code saves the context Because you are going to add a validation rule that includes the priority, there is a possibility that the new priority could fail the validation If the validation fails, the save method will fail, so you need to roll the context back to its state before the failed save If the save method fails, you revert the priority back to its original state using the rollback method of the context The rollback method undoes all changes to the context that have not yet been committed with a successful save call If an error occurs, such as a validation failure, you show the user an alert to inform him that a problem has occurred If there is no problem, the code pops the View Controller from the stack Finally, implement the dealloc method to release the member variables that you allocated in the class: - (void)dealloc { [managedTaskObject release]; [managedObjectContext release]; [super dealloc]; } EditPriorityController.m CH007.indd 188 9/18/10 9:48:46 AM ❘ 189 Building the Editing Controllers Adding and Editing Locations with the EditLocationController The user navigates to the EditLocationController by tapping the location cell on the ViewTaskController The EditLocationController, as shown in Figure 7-9, allows the user to select a location, add new locations, and delete existing locations To create the EditLocationController, create a new UITableviewController without a NIB called EditLocationController Modify your new header fi le to create instance variables and properties to hold the context and Task objects that the parent screen will configure You will also need to add a member variable and property for the NSFetchedResultsController that you will use to display your location list Additionally, you will need to add #import directives for the Task and Location header fi les The completed header fi le should look like Listing 7-5 FIGURE 7- 9: EditLocation Controller Screen LISTING 7-5: EditLocationController.h #import #import “Task.h” #import “Location.h” @interface EditLocationController : UITableViewController { NSFetchedResultsController *fetchedResultsController; NSManagedObjectContext *managedObjectContext; Task* managedTaskObject; } @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain) Task* managedTaskObject; @end Let’s move on to the implementation fi le Add an import statement for EditTextController.h: #import “EditTextController.h” You will use the EditTextController to add the text for newly added locations Next, synthesize the properties that you declared in the header fi le: @synthesize fetchedResultsController, managedObjectContext, managedTaskObject; CH007.indd 189 9/18/10 9:48:46 AM 190 ❘ CHAPTER BUILDING A CORE DATA APPLICATION Uncomment and implement the viewDidLoad method: - (void)viewDidLoad { [super viewDidLoad]; // Set up the add button UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewLocation)]; self.navigationItem.rightBarButtonItem = addButton; [addButton release]; NSError* error; if (![[self fetchedResultsController] performFetch:&error]) { NSLog(@”Unresolved error %@, %@”, error, [error userInfo]); abort(); } // set the title to display in the nav bar self.title = @”Location”; } EditLocationController.m This code creates the addButton and sets it to call the insertNewLocation method It then adds the addButton to the nav bar Next, you tell the fetched results controller to fetch its data Finally, you set the title of the screen to Location Next, you need to set the class properties in the viewDidUnload method to nil: - (void)viewDidUnload { self.fetchedResultsController=nil; self.managedTaskObject=nil; self.managedObjectContext=nil; [super viewDidUnload]; } EditLocationController.m Now, add the insertNewLocation method that runs when the user taps the Add button that you created in viewDidLoad This method adds a new Location to the context and pushes the text controller on to the navigation stack to allow the user to edit the location name Here is the insertNewLocation method: - (void)insertNewLocation { NSManagedObjectContext *context = self.managedObjectContext; Location *newLocation = CH007.indd 190 9/18/10 9:48:47 AM bindex.indd 377 9/17/10 7:26:14 PM bindex.indd 378 9/17/10 7:26:14 PM