Using SQLite: Fetch and Display The Database Records

Last week you learned how to add records in the Database. Today you will you will learn how to fetch records from the database and display them in the Master view’s Table View control.

Fetch Records From The Database

I want you to declare and implement this method in the AppDelegate class. It job is to fetch all records from the database’s possessions table, add them in a mutable array, then return the array to the caller of the method.

- (NSMutableArray *) retrievePossessions
{
  // This array is for holding records fetched from the database's possessions table
  NSMutableArray *dbRecords = [[NSMutableArray alloc] init];
  NSData *dbImage = [[NSData alloc] init];

  const char *sql = "SELECT name, description, photo FROM possessions";
  sqlite3_stmt *compiledStatement;

  //1. Prepare the sql statement
  int result = sqlite3_prepare_v2(dbHandle, sql, -1, &compiledStatement, NULL);

  if(result != SQLITE_OK) {
    NSLog(@"retrievePossessions Error:\n%s", sqlite3_errmsg(dbHandle));
  } else {
    //2. Execute the prepared statement
    while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
      // Convert the record's columns to Objective-C objects before assigning them to object variables
      //NSNumber *itemId = [NSNumber numberWithInt:sqlite3_column_int(compiledStatement, 0)];
      NSString *itemName = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(compiledStatement, 1)];
      NSString *itemDescription = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(compiledStatement, 2)];

      const char *binaryData = sqlite3_column_blob(compiledStatement, 3);
      int dataLength = sqlite3_column_bytes(compiledStatement, 3);
      dbImage = [NSData dataWithBytes:binaryData length:dataLength];

      // Instantiate an object from the Item class
      Item *record = [[Item alloc] init];

      // Initialize the object's properties
      record.itemName = itemName;
      record.itemDescription = itemDescription;
      record.itemImage = [UIImage imageWithData:dbImage];

      // Add the object in the mutable array
      [dbRecords addObject:record];
    }

  //3. Finalize the prepared statement
  sqlite3_finalize(compiledStatement);

  return dbRecords;
}

Display The Database Records

Now, I want you to modify the MasterViewController.h file’s code to what’s shown below.

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

@interface MasterViewController : UITableViewController

@property (strong, nonatomic) NSMutableArray *itemsOwned;
@property (strong, nonatomic) AppDelegate *appDelegate;

@end

Next, add this import statement in the MasterViewController.m file

#import "DetailViewController.h"

Add code shown below in the MasterViewController.m file method.

- (void)viewDidLoad
{
  [super viewDidLoad];
  // A pointer variable to the AppDelegate class so we can access its properties and methods
  self.appDelegate = [[UIApplication sharedApplication] delegate];

  // Fetch all records from the database file and place them in the mutable array
  [self.appDelegate openDatabase];
  self.itemsOwned = [self.appDelegate retrievePossessions];
  [self.appDelegate closeDatabase];
}

Add source code shown below in the Table View’s methods.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
  return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
  return self.itemsOwned.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];

  // Instantiate and initialize an Item object
  Item *row = [[Item alloc] init];
  row = self.itemsOwned[indexPath.row];

  // Add the object's itemName property in the table row and return
  cell.textLabel.text = row.itemName;
  return cell;
}

Test The App

Take the app for a spine and you will see the Item’s name in the Master View control.

sqlitedatabase-fig11b

Pass The Item to The Detail View

What you need to do now is add this code in the MasterViewController.m files’s prepareForSegue method. Code in the if() statement block basically pass the Item Name the user select from the Master view’s Table View control, to the DetailViewController’s dbItem property.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
  NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
  if ([segue.identifier isEqualToString:@"showDetail"]) {
    DetailViewController *destination = segue.destinationViewController;
    destination.dbItem = self.itemsOwned[indexPath.row];
  }
}

In the storyboard file, I’ve already set up the segue for the Table View cell object and given it a name, showDetail.

The Detail View Code

What you need to do now is add code in the DetailViewController class to populate the view’s controls and enable the user to select an photo from the simulator Photos app’s Photo Library. Start by modifying the DetailViewController.h file to this:

sqlitedatabase-fig15
Figure 1

Now, add this code in the DetailViewcController.m file’s viewDidLoad method.

- (void)viewDidLoad
{
  [super viewDidLoad];
  // Populate the view's controls
  self.itemName.text = self.dbItem.itemName;
  self.itemDescription.text = self.dbItem.itemDescription;
 self.imageView.image = self.dbItem.itemImage;
}

Next, add this code in the chooseImageButtonTapped method.

- (IBAction)chooseImageButtonTapped {
  UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];

  // Set the imagePicker delegate so we can use its delegate methods
  imagePicker.delegate = self;

  // Set the imagePicker object to access the Photos app's Photo Library
  imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

  // Launch the Photos app
 [self presentViewController:imagePicker animated:YES completion:nil];
}

Here is the code to implement two methods of the UIImagePickerControllerDelegate class.

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
  // Declare an image object, set it to the image the user picked
  UIImage *chosenImage = [info valueForKey:UIImagePickerControllerOriginalImage];

  // Set the view's ImageView control's Image property with the image the user picked
  self.imageView.image = chosenImage;

  // The user finished picking an image, so dismiss the imagePicker
  [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
  // The user tapped the imagePicker's Cancel button, so dismiss the imagePicker
  [self dismissViewControllerAnimated:YES completion:Nil];
}

Test The DetailViewController Code

Now, run the program, click a row of the Table View control-see Figure 2, the detail view will load in the simulator’s window and it will look like Figure 3 below. Go ahead click the Choose Image button to select and a different photo for the item.

sqlitedatabase-fig11b sqlitedatabase-fig16
Figure 2 Figure 3

What you need to do now is add code Detail view’s Update button’s method to update the record in the database for the item shown in the view’s controls. You’ll learn how to do that next week.