iOS

iOS Data: Wading through CRUD

Every iOS developer planning to work with data should become familiar with the acronym CRUD (create, read, update, delete).

After five short years, the Apple App Store has grown from an initial offering of 500 iPhone apps to an awe-inspiring mobile app mecca hosting over 900,000 titles. It is fair to say that a large percentage of today's apps need to store information. Many iOS apps are completely data-driven. The June 2013 TechRepublic article, Gain a basic understanding of iOS Core Data, explains the different approaches for persisting data between app launches. A basic understanding, however, will only fuel the decision to use Core Data. Every iOS developer planning to work with data should become familiar with the acronym CRUD (create, read, update, delete).

The core data app project

Before you can create, read, update, or delete information, a data model must be developed and linked to an iOS project. Additionally, the Core Data framework must be included. If you select the "Use Core Data" option when creating a new project, all of the necessary files and frameworks will be automatically created and linked (Figure A).

Figure A

a_gdean_crud.png

A data model is described within a special type of file. The .xcdatamodeld document is actually a file package that contains individual .xcdatamodel and .plist files for versioning. This concept is described in more detail on the Apple Developer website. Entities, attributes, and relationships need to be created for the purpose of describing the layout of the information being stored. Using common database terminology, entities can be thought of as tables, and attributes as fields. An app developed to track information about a user's favorite wines, for example, would include a data model designed to store wine-centric information - such as wine name, vintage, cost, type, and description.

Developing a data model

Selecting an .xcdatamodeld object from the project navigator pane opens the Core Data Model Editor. This interface allows for managing entities, attributes, and relationships (Figure B).

Figure B

b_gdean_crud.png

The core data model editor is comprised of two main sections - a components area and a detail area. The components area displays top-level information such as entities, fetch requests, and configurations. The detail area exposes information about the data model components. Every core data model begins with an entity.

To create an entity, click on the "Add Entity" icon at the bottom of the core data model editor window. Using the wine app scenario, create a new entity and name it "Wine" (Figure C).

Figure C

c_gdean_crud.png

With the entity selected, attributes for storing specific pieces of information need to be added. To create an attribute, click on the "Add Attribute" icon at the bottom of the core data model editor window. Each attribute consists of a name and a data type. The list of standard attribute types include:

  • Integer 16
  • Integer 32
  • Integer 64
  • Decimal
  • Double
  • Float
  • String
  • Boolean
  • Date
  • Binary Data
  • Transformable

The attribute type chosen will depend on the information to be stored. In the case of a wine tracking iOS app, information will include dates, text, and numbers. The rating is represented by a numeric value and stored as an Integer 16. The name and description of a wine would be stored as a string. For the purpose of explaining the process of creating, reading, updating and deleting information, a basic set of attributes describing wine will be used (Figure D).

Figure D

d_gdean_crud.png

Creating a class file from the data model

The Xcode environment makes it easy to generate a class file to represent each core data entity. The class files are used describe the data for CRUD operations. With the "Wine" entity selected, choose File -| New -| File from the menu bar. Choose the NSManagedSubclass templare. Two new files will be added to the project - Wine.m and Wine.h (Figure E).

Figure E

e_gdean_crud.png

Behind the CRUD

The core data services classes necessary for all CRUD operations include:

  • NSManagedObjectContext,
  • NSManagedObjectModel, and
  • NSPersistentStoreCoordinator.

Each of these three classes provide specific functionality. The NSManagedObjectContext class is described by Apple as a "scratch pad" where changes happen. The CRUD operations - create, read, update and delete operations - are not persisted until the save method is called. The NSManagedObjectModel describes the collection of data objects used in an iOS app. The NSPersistentStoreCoordinator is the liaison between the two core data classes - NSManagedObjectModel and NSManagedObjectContext - and the database.

There are a few simple steps that are required to prepare an iOS app to perform CRUD operations. The classes described above need to be added to the app's delegate header file. Step one is to edit the AppDelegate.h file (Listing 1.1).

Listing 1.1

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

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

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

The second step is to edit the app's delegate main file - AppDelegate.m.  Include the data model class (i.e. #import "Wine.h"). Make sure to @synthesize the objects added to the app delegate header file (Listing 1.2).

Listing 1.2

@synthesize managedObjectContext = _managedObjectContext;

@synthesize managedObjectModel = _managedObjectModel;

@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

With the core data classes in place, the methods for creating, reading, updating, and deleting can be added. Typically, information would be captured on form fields and added to the database. To illustrate the behind-the-scene process supporting CRUD operations, the data is "hard-coded" in the following explanations.

Create

Any method to add new records to the database will work within an instance of NSManagedObjectContext. There are four simple steps for adding new records to a database (Listing 1.3).

  1. Reference the NSManagedObjectContext.
  2. Create an instance of the entity class.
  3. Populate the attributes.
  4. Save the data using the NSManagedObjectContext.save() method.

Listing 1.3

- (void) createRecord {

  // Reference the context

  NSManagedObjectContext *context = [self managedObjectContext];

 

  // Reference our data class

  Wine *wine = [NSEntityDescription insertNewObjectForEntityForName:@"Wine" inManagedObjectContext:context];

 

  // Assign values to the attributes

  wine.wine_id = @1;

 

  // Set the wine name

  wine.wine_name = @"Silver Oak - Alexander Valley";

 

  // Set the wine vintage

  wine.wine_vintage = @"2007";

 

  // Set the wine description

  wine.wine_description = @"Outstanding wine. My favorite";

 

  // Set the wine rating

  wine.wine_rating = @5;

 

  // Save the record so the data persists

  NSError *error = nil;

  if ([context save:&error]) {

  NSLog(@"New wine information was saved!");

  } else {

  NSLog(@"The wine information was not saved: %@", [error userInfo]);

  }

}

Read

To read information from a core data entity, you simply reference the NSManagedObjectContext, construct a fetch request and query the entity object. The steps include (Listing 1.4):

  1. Reference the NSManagedObjectContext.
  2. Construct a fetch request.
  3. Create an entity description object.
  4. Retrieve the data objects into an array.
  5. Display the results.

Listing 1.4

- (void) readData {

  // Reference the context

  NSManagedObjectContext *context = [self managedObjectContext];

 

  // Build a fetch request

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

  NSEntityDescription *entity = [NSEntityDescription entityForName:@"Wine" inManagedObjectContext:context];

 

  [fetchRequest setEntity:entity];

  NSError *error = nil;

  NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];

 

  for (Wine *wine in fetchedObjects) {

  // For each record retrieved, output to the log

  NSLog(@"%@: %@ %@ (%@)", wine.wine_id, wine.wine_vintage, wine.wine_name, wine.wine_description);

  }

}

Update

From time to time, information stored in the database will need to be updated. Using the wine tracking app database as an example, we might want to change the description for a particular wine. The steps to update information closely resemble the process for reading data.

  1. Reference the NSManagedObjectContext.
  2. Construct a fetch request.
  3. Create an entity description object.
  4. Retrieve the data objects into an array.
  5. Assign new values to the attributes
  6. Commit the changes (save the data).

Delete

Removing records from a database is pretty straight forward. We start by referencing the context. Then create a fetch request and use an NSPredicate object to filter the results. With the desired records retrieved, we call the NSManagedObjectContext.deleteObject() method. Finally, we commit the changes (Listing 1.5).

Listing 1.5

- (void) deleteData {

  // Reference the context

  NSManagedObjectContext *context = [self managedObjectContext];

 

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

  NSEntityDescription *entity = [NSEntityDescription entityForName:@"Wine"

  inManagedObjectContext:context];

 

  [fetchRequest setEntity:entity];

 

  // We specify that we only want Silver Oak

  NSPredicate *predicate = [NSPredicate predicateWithFormat:@"wine_name == %@", @"Silver Oak"];

  [fetchRequest setPredicate:predicate];

 

  NSError *error = nil;

  NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];

 

  // Grab the wine and delete the record

  Wine *wine = [fetchedObjects objectAtIndex:0];

  [context deleteObject:wine];

 

  // Commit the changes

  if ([context save:&error]) {

  NSLog(@"Did it!");

  } else {

  NSLog(@"Could not do it: %@", [error localizedDescription]);

  }

}

Conclusion

The core data framework makes it easy to work with data. CRUD operations are straight-forward. Even the more complex tasks - such as cascading deletes - require little effort by the developer. However, let's not discount the importance of good database design. The performance of a data-driven iOS app can be greatly affected by a bad database design.

Data manipulation on an iOS device can be intimidating without an understanding of core data. At the end of the day, however, the "CRUD puddle" is not very deep. Go ahead, jump in!


About

Gregory Dean is the CTO for the Wilen Group. He has spent the past 25 years in C-level positions setting the tone and direction of technology-driven services organizations. Gregory is constantly researching technologies and developing capabilities to...

0 comments

Editor's Picks