Using Core Data: Workshop 6

This is the final workshop in the Using Core Data series. You will learn how to implement the ability for the app user to update attributes of a managed object in the persistent store.

Setup The Book Detail Scene

In order to enable app user to update attributes of a managed object in the persistent store, you’ll have to first set up the Book Detail scene.

  • Drag a View Controller object from the Object Library and drop it on the storyboard canvas.
  • Copy controls from the AddBookViewController scene and past them on the new scene.
  • Set the Image View control’s Mode property to Aspect Fit.
  • Configure the scene’s navigation bar to what’s shown in Figure A below.
coredata-scene3
Figure A

Setup The Book Detail Scene’s Class

Now that you’ve setup the Book Detail scene, you need to set up the scene’s class.

  • Use Xcode’s template to add an Objective-C class in the project.
  • Make it a subclass of the View Controller class and name it BookDetailViewController.
  • Connect the class to the Book Detail scene via Interface Builder’s Identity Inspector panel.
  • In the Attributes Inspector panel, connect each text field’s delegate property.
  • Connect the BookDetailViewController scene to the BookViewController scene’s Table View Cell by creating a Push segue in Interface Builder.
  • Set the segue’s Identifier attribute to updateBook.

Figure B shows what the Project Navigator and the storyboard should look like now in Interface Builder.

coredata-projnav-storyboard
Figure B

The BookDetailViewController Class Code

At this point in time you need to add code in the BookDetailViewController class files to make its scene functional. Start by adding code shown below in the class header file. Then connect the view’s controls and the navigation bar’s Update button in Interface Builder.

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

@interface BookDetailViewController : UIViewController

// A pointer for accessing the AppDelegate class properties and methods
@property (strong, nonatomic) AppDelegate *appDelegate;

// Properties for the view's controls
@property (strong, nonatomic) IBOutlet UITextField *bookTitle;
@property (strong, nonatomic) IBOutlet UITextField *authorName;
@property (strong, nonatomic) IBOutlet UITextField *bookCoverImageUrl;
@property (strong, nonatomic) IBOutlet UIImageView *bookCoverImageView;

// A property for holding the url of an image
@property (strong, nonatomic) NSString *noImageUrl;

// These variables are for holding details of book title the user selected from the Book List scene
@property (strong, nonatomic) NSArray *bookObject;
@property (strong, nonatomic) NSString *oldBookTitle;

// The navigation bar's Save button action method
- (IBAction)updateButtonClicked;

@end

What you need to do now is uncomment the BookListViewController.m file’s prepareForSegue: method and add code shown below in the method. You must import the BookDetailViewController.h file in the BookListViewController.m file as well.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
  if ([[segue identifier] isEqualToString:@"updateBook"]) {
    NSArray *selectedBook = [self.tableData objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
    BookDetailViewController *destination = segue.destinationViewController;
    destination.bookObject = selectedBook;
  }
}

You entered code in the prepareForSegue: method to basically pass a book object called selectedBook, to the BookDetailViewController class. The book object that’s passed to the BookDetailViewController class is determined by the book title the user selected from the BookListViewController scene’s table view control-see code line 04 above.

What you need to do now is add code in the BookDetailViewController.m file’s viewDidLoad method to handle these task:

  • Initialize the appDelegate property variable, so we can access the AppDelegate class properties and variable.
  • Set the BookDetailViewController scene’s controls with elements of the bookObject.
- (void)viewDidLoad
{
  [super viewDidLoad];
  self.appDelegate = [[UIApplication sharedApplication] delegate];

  if (self.bookObject) {
    // Set the view's controls
    self.bookTitle.text = self.bookObject[0];
    self.authorName.text = self.bookObject[1];
    self.bookCoverImageUrl.text = self.bookObject[2];
    self.bookCoverImageView.image = [self.appDelegate extractImageFromUrl:self.bookCoverImageUrl.text];
    self.oldBookTitle = self.bookObject[0];
  }
}

Here’s the code to add in the touchesBegan and textFieldShouldReturn methods.

// This method is fired when the user tap the view's background
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
  // Initialize the Image View control's image property with an image extracted from URL provided in the third Text Field control
  self.bookCoverImageView.image = [self.appDelegate extractImageFromUrl:self.bookCoverImageUrl.text];

  // Dismiss the keyboard
  [self.view endEditing:YES];
}

// This method is fired when the user tap the keyboard's return key
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
  // Initialize the Image View control's image property with an image extracted from URL provided in the third Text Field control
  self.bookCoverImageView.image = [self.appDelegate extractImageFromUrl:self.bookCoverImageUrl.text];

  // Dismiss the keyboard
  [textField resignFirstResponder];
  return YES;
}

Test The Update Book User Interface

Now is a good time to test the Update Book user interface. Run the application; when the Book List view is loaded in the simulator’s window (Figure C), click a row and the Book Detail view will be loaded in the simulator’s window, as shown in Figure D below.

coredata-fig24 coredata-fig24d
Figure C Figure D

As of now, nothing happens when you click the Book Detail view’s Update button. To rectify this, you’ll have to add code in the BookDetailViewController.m file’s updateButtonClicked method to handle these tasks:

  • Clean the first two text field controls.
  • Validate the first two text field controls. Display an error message in an alert view if the user failed to enter a title or the author’s name in the first two text field controls.
  • Update a managed object in the persistent store for the book title provided in the oldBookTitle variable. Display a success message in an alert view.

Here is the source code to enter in the updateButtonClicked method, which handle tasks listed above.

- (IBAction)updateButtonClicked
{
  //1. Clean text field controls
  NSString *cleanBookTitle = [self.bookTitle.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  NSString *cleanAuthorName = [self.authorName.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

  //2. Validate the first two text field controls
  if (cleanBookTitle.length == 0) {
    UIAlertView *alert = [[UIAlertView alloc]
    initWithTitle:@"Required Data Missing."
    message:@"Book Tittle"
    delegate: nil
    cancelButtonTitle: @"OK"
    otherButtonTitles: nil];
    [alert show];
  } else if (cleanAuthorName.length == 0) {
    UIAlertView *alert = [[UIAlertView alloc]
    initWithTitle:@"Required Data Missing."
    message:@"Author Name"
    delegate: nil
    cancelButtonTitle: @"OK"
    otherButtonTitles: nil];
    [alert show];
  } else {
    //3. Update a managed object in the persistent store for the book title provided in the oldBookTitle variable
    NSArray *book = @[self.bookTitle.text, self.authorName.text, self.bookCoverImageUrl.text];
    [self.appDelegate updateBook:self.oldBookTitle field2:book];

    // Display a success message in an Alert View object
    UIAlertView *alert = [[UIAlertView alloc]
    initWithTitle:@"Update Successful!"
    message:@"The book was updated successfully in the database."
    delegate: nil
    cancelButtonTitle: @"OK"
    otherButtonTitles: nil];
    [alert show];
  }
}

Take a look at code line 27 in above method. As you can see, we are using a method called updateBook. Its job is to update a managed object in the persistent store for the book title provided in the oldBookTitle variable.

Now, here is the source code to implement the updateBook: method in the AppDelegate.m file. Don’t forget to declare it in the AppDelegate.h file as well.

- (void) updateBook:(NSString *)oldBookTitle field2:(NSArray *)newBook
{
  // Create and initialize some variables
  NSError *error = nil;
  NSArray *fetchedObject = [[NSArray alloc] init];

  //1. Create a managed object context object
  NSManagedObjectContext *context = [self managedObjectContext];

  //2 Setup a fetch request object
  NSFetchRequest *request = [[NSFetchRequest alloc] init];
  request.entity = [NSEntityDescription entityForName:@"Book" inManagedObjectContext:context];

  //3. Set up a predicate (fetch filter condition)
  request.predicate = [NSPredicate predicateWithFormat:@"title = %@", oldBookTitle];

  //4. Execute the fetch request
  fetchedObject = [context executeFetchRequest:request error:&error];
  if ([fetchedObject count] > 0) {
    //5. Set the fetchedObject's properties
    [fetchedObject setValue:newBook[0] forKey:@"title"];
    [fetchedObject setValue:newBook[1] forKey:@"author"];
    [fetchedObject setValue:newBook[2] forKey:@"imageUrl"];
  }
  // Save the object to the persistent store
  if (![context save:&error]) {
    NSLog(@"Can't Save! %@", error);
  }
}

Test The Update Button’s Code

Once you’ve implemented the updateBook: method in the AppDelegate.m file, run the application and test the Book Detail view’s Update button code. Images below show what you’ll see in the simulator window.

coredata-fig24 coredata-fig24b coredat-fig24c

Summary

This conclude the Using Core Data workshop series. You learned how to do the follow:

  • Add the Core Data framework in the project
  • Set up the Core Data stack in the AppDelegate class
  • Include the CoreData.h file in the AppDelegate class
  • Add a data model file in the project, then add a Book entity in it
  • Extract an image from an url and load it in an Image View control

You also learned how to use class and methods of the Core Data framework to enable the user to perform these operations:

  • Create managed objects in the persistent store, which is a SQLite database file
  • Fetch a single managed object from the persistent store
  • Fetch all managed objects from the persistent store
  • Update a managed object in the persistent store
  • delete a managed object from the persistent store