Create a Data Class To get started, open

Create a Data Class

To get started, open your existing project in Xcode.

At this point, you have an interface and a navigation scheme for your ToDoList app using storyboards. Now, it’s time to add data storage and behavior with model objects.

The goal of your app is to create a list of to-do items, so first you’ll create a custom class, XYZToDoItem. to represent an individual to-do item. As you recall, the XYZToDoItem class was discussed in Writing a Custom Class .

A dialog appears that prompts you to choose a template for your new file.

On the left, select Cocoa Touch under iOS.

Select Objective-C Class, and click Next.

Choose NSObject from the “Subclass of” pop-up menu.

If you’ve been following along with the tutorials exactly, the Class title probably said XYZToDoItemViewController prior to this step. When you choose NSObject as the “Subclass of,” Xcode knows you’re making a normal custom class and removes the ViewController text that it was adding previously.

The save location defaults to your project directory. Leave that as is.

The Group option defaults to your app name, ToDoList. Leave that as is.

The Targets section defaults to having your app selected and the tests for your app unselected. That’s perfect, so leave that as is.

The XYZToDoItem class is straightforward to implement. It has properties for its name, creation date, and whether the item has been completed. Go ahead and add these properties to the XYZToDoItem class interface.

Add the following properties to the interface so that the declaration looks like this:

  1. @interface XYZToDoItem. NSObject
  2. @property NSString * itemName ;
  3. @property BOOL completed ;
  4. @property ( readonly ) NSDate * creationDate ;
  5. @end

Checkpoint: Build your project by choosing Product > Build (or pressing Command-B). You’re not using your new class for anything yet, but building it gives the compiler a chance to verify that you haven’t made any typing mistakes. If you have, fix them by reading through the warnings or errors that the compiler provides, and then look back over the instructions in this tutorial to make sure everything looks the way it’s described here.

Load the Data

You now have a class from which you can create and store the data for individual list items. You also need to keep a list of those items. The natural place to track this is in the XYZToDoListTableViewController class—view controllers are responsible for coordinating between the model and the view, so they need a reference to the model.

The Foundation framework includes a class, NSMutableArray. that works well for tracking lists of items. It’s important to use a mutable array so that the user can add items to the array. The immutable version, NSArray. doesn’t allow you to add items to it after it’s initialized.

To use an array you need to both declare it and create it. You do this by allocating and initializing the array.

To allocate and initialize the array

In the project navigator, select XYZToDoListTableViewController.m .

Because the array of items is an implementation detail of your table view controller, you declare it in the .m file instead of the .h file. This makes it private to your custom class.

Add the following property to the interface category Xcode created in your custom table view controller class. The declaration should look like this:

  1. @interface XYZToDoListTableViewController ()
  2. @property NSMutableArray * toDoItems ;
  3. @end

Allocate and initialize the toDoItems array in the viewDidLoad method:

  1. – ( void ) viewDidLoad
  2. <
  3. [ super viewDidLoad ];
  4. self. toDoItems = [[ NSMutableArray alloc ] init ];
  5. >

The actual code for viewDidLoad includes some additional lines—inserted by Xcode when it created XYZListViewController —that are commented out. Feel free to leave them in.

At this point, you have an array that you can add items to. You’ll do this in a separate method, loadInitialData. which you’ll call from viewDidLoad. This code goes in its own method because it’s a modular task, and you can improve code readability by making this method separate. In a real app this method would load the data from some sort of persistent store, such as a file. For now, the goal is to see how a table view works with custom data items, so you’ll create some test data to experiment with.

Create an item in the way you created the array: Allocate and initialize. Then, give the item a name. This is the name that will be shown in the table view. Do this for a couple of items.

To load initial data

Add a new method, loadInitialData. below the @implementation line.

  1. – ( void ) loadInitialData <
  2. >

In this method, create a few list items, and add them to the array.

  1. – ( void ) loadInitialData <
  2. XYZToDoItem * item1 = [[ XYZToDoItem alloc ] init ];
  3. item1. itemName = @"Buy milk" ;
  4. [ self. toDoItems addObject: item1 ];
  5. XYZToDoItem * item2 = [[ XYZToDoItem alloc ] init ];
  6. item2. itemName = @"Buy eggs" ;
  7. [ self. toDoItems addObject: item2 ];
  8. XYZToDoItem * item3 = [[ XYZToDoItem alloc ] init ];
  9. item3. itemName = @"Read a book" ;
  10. [ self. toDoItems addObject: item3 ];
  11. >

Call the loadInitialData in the viewDidLoad method.

  1. – ( void ) viewDidLoad
  2. <
  3. [ super viewDidLoad ];
  4. self. toDoItems = [[ NSMutableArray alloc ] init ];
  5. [ self loadInitialData ];
  6. >

Checkpoint: Build your project by choosing Product > Build. You should see numerous errors for the lines of your loadInitialData method. The key to what’s gone wrong is the first line, which should say “Use of undeclared identifier XYZToDoItem.” This means that the compiler doesn’t know about your XYZToDoItem when it’s compiling XYZToDoListTableViewController. Compilers are very particular and need to be told explicitly what to pay attention to.

Add the following line immediately below it:

  1. #import "XYZToDoItem.h"

Checkpoint: Build your project by choosing Product > Build. It should build without errors.

Display the Data

At this point, your table view has a mutable array that’s prepopulated with some sample to-do items. Now you need to display the data in the table view.

You’ll do this by making XYZToDoListTableViewController a data source of the table view. To make something a data source of the table view, it needs to implement the UITableViewDataSource protocol. It turns out that the methods you need to implement are exactly the ones you commented out in the second tutorial. To have a functioning table view requires three methods. The first of these is numberOfSectionsInTableView. which tells the table view how many sections to display. For this app, you want the table view to display a single section, so the implementation is straightforward.

In the project navigator, select XYZToDoListTableViewController.m .

If you commented out the table view data source methods in the second tutorial, remove those comment markers now.

Find the section of the template implementation that looks like this.

  1. – ( NSInteger ) numberOfSectionsInTableView: ( UITableView * ) tableView
  2. <
  3. #warning Potentially incomplete method implementation.
  4. // Return the number of sections.
  5. return 0 ;
  6. >

You want a single section, so you want to remove the warning line and change the return value from 0 to 1.

The next method, tableView:numberOfRowsInSection. tells the table view how many rows to display in a given section. You have a single section in your table, and each to-do item should have its own row in the table view. That means that the number of rows should be the number of XYZToDoItem objects in your toDoItems array.

In the project navigator, select XYZToDoListTableViewController.m .

Find the section of the template implementation that looks like this:

  1. – ( NSInteger ) tableView: ( UITableView * ) tableView numberOfRowsInSection: ( NSInteger ) section
  2. <
  3. #warning Incomplete method implementation.
  4. // Return the number of rows in the section.
  5. return 0 ;
  6. >

You want to return the number of list items you have. Fortunately, NSArray has a handy method called count that returns the number of items in the array, so the number of rows is [self.toDoItems count] .

Change the tableView:numberOfRowsInSection: data source method to return the appropriate number of rows.

  1. – ( NSInteger ) tableView: ( UITableView * ) tableView numberOfRowsInSection: ( NSInteger ) section
  2. <
  3. // Return the number of rows in the section.
  4. return [ self. toDoItems count ];
  5. >

The last method, tableView:cellForRowAtIndexPath. asks for a cell to display for a given row. Up until now, you’ve been working with code only, but the cell to display for a row is very much part of your interface. Fortunately, Xcode makes it easy to design custom cells in Interface Builder. The first task is to design your cell and to tell the table view that instead of using static content, it’s going to be using prototype cells with dynamic content.

To configure your table view

Open your storyboard.

Select the table view in the outline.

With the table view selected, open the Attributes inspector in the utility area.

In the Attributes inspector, change the table view’s Content attribute from Static Cells to Dynamic Prototypes.

Interface Builder takes the static cells you configured and converts them all into prototypes. Prototype cells, as the name implies, are cells that are configured with text styles, colors, images, or other attributes as you want them to be displayed but that get their data from the data source at runtime. The data source loads a prototype cell for each row and then configures that cell to display the data for the row.

To load the correct cell, the data source needs to know what it’s called, and that name must also be configured in the storyboard.

While you’re setting the prototype cell name, you’ll also configure another property—the cell selection style, which determines a cell’s appearance when a user taps it. Set the cell selection style to None so that the cell won’t be highlighted when a user taps it. This is the behavior you want your cells to have when a user taps an item in the to-do list to mark it as completed or uncompleted—a feature you’ll implement later in this tutorial.

To configure the prototype cell

Select the first table view cell in your table.

In the Attributes inspector, locate the Identifier field and type ListPrototypeCell .

In the Attributes inspector, locate the Selection field and choose None.

You could also change the font or other attributes of the prototype cell. The basic configuration is easy to work with, so you’ll keep that.

The next step is to teach your data source how to configure the cell for a given row by implementing tableView:cellForRowAtIndexPath. This data source method is called by the table view when it wants to display a given row. For table views with a small number of rows, all rows may be onscreen at once, so this method gets called for each row in your table. But table views with a large number of rows display only a small fraction of their total items at a given time. It’s most efficient for table views to only ask for the cell for rows that are being displayed, and that’s what tableView:cellForRowAtIndexPath: allows the table view to do.

For any given row in the table, fetch the corresponding entry in the toDoItems array and then set the cell’s text label to the item’s name.

To display cells in your table

In the project navigator, select XYZToDoListTableViewController.m .

Find the tableView:cellForRowAtIndexPath: data source method. The template implementation looks like this:

  1. – ( UITableViewCell * ) tableView: ( UITableView * ) tableView cellForRowAtIndexPath: ( NSIndexPath * ) indexPath
  2. <
  3. static NSString * CellIdentifier = @"Cell" ;
  4. UITableViewCell * cell = [ tableView dequeueReusableCellWithIdentifier: CellIdentifier forIndexPath: indexPath ];
  5. // Configure the cell.
  6. return cell ;
  7. >

The template performs several tasks. It creates a variable to hold the identifier for the cell, asks the table view for a cell with that identifier, adds a comment about where code to configure the cell should go, and then returns the cell.

To make this code work for your app, you’ll need to change the identifier to the one you set in the storyboard and then add code to configure the cell.

Change the cell identifier to the one you set in the storyboard. To avoid typos, copy and paste from the storyboard to the implementation file. The cell identifier line should now look like this:

  1. static NSString * CellIdentifier = @"ListPrototypeCell" ;

Just before the return statement, add the following lines of code:

  1. XYZToDoItem * toDoItem = [ self. toDoItems objectAtIndex: indexPath. row ];
  2. cell. textLabel. text = toDoItem. itemName ;

Your tableView:cellForRowAtIndexPath: method should look like this:

  1. – ( UITableViewCell * ) tableView: ( UITableView * ) tableView cellForRowAtIndexPath: ( NSIndexPath * ) indexPath
  2. <
  3. static NSString * CellIdentifier = @"ListPrototypeCell" ;
  4. UITableViewCell * cell = [ tableView dequeueReusableCellWithIdentifier: CellIdentifier forIndexPath: indexPath ];
  5. XYZToDoItem * toDoItem = [ self. toDoItems objectAtIndex: indexPath. row ];
  6. cell. textLabel. text = toDoItem. itemName ;
  7. return cell ;
  8. >

Checkpoint: Run your app. The list of items you added in loadInitialData should show up as cells in your table view.

Mark Items as Completed

A to-do list isn’t much good if you can never mark items as completed. Now, you’ll add support for that. A simple interface would be to have the completion state toggle when the user taps the cell and to display completed items with a checkmark next to them. Fortunately, table views come with some built-in behavior that you can take advantage of to implement this simple interface—specifically, table views notify their delegate when the user taps a cell. So the task is to write the code that will respond to the user tapping a to-do item in the table.

Xcode already made XYZToDoListTableViewController the delegate of the table view when you configured it in the storyboard. All you have to do is implement the tableView:didSelectRowAtIndexPath: delegate method to respond to user taps and update your to-do list items appropriately.

When a cell gets selected, the table view calls the tableView:didSelectRowAtIndexPath: delegate method to see how it should handle the selection. In this method, you’ll write code to update the to-do item’s completion state.

To mark an item as completed or uncompleted

In the project navigator, select XYZToDoListTableViewController.m .

Add the following lines to the end of the file, just above the @end line:

  1. #pragma mark – Table view delegate
  2. – ( void ) tableView: ( UITableView * ) tableView didSelectRowAtIndexPath: ( NSIndexPath * ) indexPath
  3. <
  4. >

Try typing the second line instead of just copying and pasting. You’ll find that code completion is one of the great time-saving features of Xcode. When Xcode brings up the list of potential completions, scroll through the list until you find the one you want and then press Return. Xcode inserts the whole line for you.

Chick this out