iOS Developer Library

Developer

Start Developing iOS Apps Today

PDF
On This Page

Tutorial: Add Data

This tutorial builds on the project you created in the second tutorial (Tutorial: Storyboards). You’ll apply what you learned about using design patterns, working with Foundation, and writing a custom class to add support for dynamic data to your ToDoList app.

This tutorial teaches you how to:

  • Work with common Foundation classes

  • Create custom data classes

  • Implement a data source and delegate protocol

  • Pass data between view controllers

After you complete all the steps in this tutorial, you’ll have an app that looks something like this:

image: ../Art/ios_simulator_add_new_item_2x.png
image: ../Art/ios_simulator_full_list_2x.png

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, ToDoItem, to represent an individual to-do item. As you recall, the ToDoItem class was discussed in Writing a Custom Class.

To create the ToDoItem class

  1. Choose File > New > File (or press Command-N).

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

  2. On the left, select Source under iOS.

  3. Select Cocoa Touch Class, and click Next.

  4. In the Class field, type ToDoItem.

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

    image: ../Art/create_item_class_2x.png
  6. Click Next.

    The save location defaults to your project directory.

    The Group option defaults to your app name, ToDoList.

    In the Targets section, your app is selected and the tests for your app are unselected.

  7. Leave these defaults as they are, and click Create.

The ToDoItem class is straightforward to implement. It has properties for its name, creation date, and completion status. Go ahead and add these properties to the ToDoItem class interface.

To configure the ToDoItem class

  1. In the project navigator, select ToDoItem.h.

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

    1. @interface ToDoItem : 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 to-do items. You also need to keep a list of those items. The natural place to track this is in the ToDoListTableViewController 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 you can add items to the array. The immutable version, NSArray, doesn’t let you 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

  1. In the project navigator, select ToDoListTableViewController.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.

  2. 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 ToDoListTableViewController ()
    2. @property NSMutableArray *toDoItems;
    3. @end
  3. Find the viewDidLoad method. The template implementation looks like this:

    1. - (void)viewDidLoad {
    2. [super viewDidLoad];
    3. // Uncomment the following line to preserve selection between presentations.
    4. // self.clearsSelectionOnViewWillAppear = NO;
    5. // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    6. // self.navigationItem.rightBarButtonItem = self.editButtonItem;
    7. }

    The template implementation of this method includes comments that were inserted by Xcode when it created ToDoListTableViewController. Code comments like this provide helpful hints and contextual information in source code files, but you don’t need them for this tutorial. Feel free to delete the comments.

  4. Update the method implementation to allocate and initialize the toDoItems array in the viewDidLoad method:

    1. - (void)viewDidLoad {
    2. [super viewDidLoad];
    3. self.toDoItems = [[NSMutableArray alloc] init];
    4. }

At this point, you have an array that you can add items to. You’ll add a few initial items 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 a persistent store, such as a file. For now, you’ll create some test data to experiment with.

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

To load initial data

  1. In ToDoListTableViewController.m, add a new method called loadInitialData below the @implementation line.

    1. - (void)loadInitialData {
    2. }
  2. In this method, create a few list items, and add them to the array.

    1. - (void)loadInitialData {
    2. ToDoItem *item1 = [[ToDoItem alloc] init];
    3. item1.itemName = @"Buy milk";
    4. [self.toDoItems addObject:item1];
    5. ToDoItem *item2 = [[ToDoItem alloc] init];
    6. item2.itemName = @"Buy eggs";
    7. [self.toDoItems addObject:item2];
    8. ToDoItem *item3 = [[ToDoItem alloc] init];
    9. item3.itemName = @"Read a book";
    10. [self.toDoItems addObject:item3];
    11. }
  3. Update the implementation of the viewDidLoad method to call the loadInitialData method.

    1. - (void)viewDidLoad {
    2. [super viewDidLoad];
    3. self.toDoItems = [[NSMutableArray alloc] init];
    4. [self loadInitialData];
    5. }

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 ToDoItem.” This means that the compiler doesn’t know about your ToDoItem class when it’s compiling ToDoListTableViewController. Compilers are very particular and need to be told explicitly what to pay attention to.

To tell the compiler to pay attention to your ToDoItem class

  1. Find the #import "ToDoListTableViewController.h" line near the top of the ToDoListTableViewController.m file.

  2. Add the following line immediately below it:

    1. #import "ToDoItem.h"

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

Display the Data

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

To display dynamic data, a table view needs two important helpers: a data source and a delegate. A table view data source, as implied by its name, supplies the table view with the data it needs to display. A table view delegate helps the table view manage cell selection, row heights, and other aspects related to displaying the data. By default, UITableViewController—and any of its subclasses—adopts the necessary protocols to make the table view controller both a data source (UITableViewDataSource protocol) and a delegate (UITableViewDelegate protocol) for its associated table view. Your job is to implement the appropriate protocol methods in your table view controller subclass so that your table view has the correct behavior.

A functioning table view requires three table view data source methods. The first of these is numberOfSectionsInTableView:, which tells the table view how many sections to display. Sections provide a way of visually grouping cells within table views, especially table views with a lot of data. For a simple table view like the one in the ToDoList app, you just need the table view to display a single section, so the implementation of the numberOfSectionsInTableView: data source method is straightforward.

To display a section in your table view

  1. In the project navigator, select ToDoListTableViewController.m.

  2. Find the numberOfSectionsInTableView: data source method. The template implementation looks like this:

    1. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    2. #warning Potentially incomplete method implementation.
    3. // Return the number of sections.
    4. return 0;
    5. }
  3. You want a single section, so change the return value from 0 to 1, and remove the warning line.

    1. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    2. // Return the number of sections.
    3. return 1;
    4. }

    You removed the warning line that says “Potentially incomplete method implementation” because you’ve completed the implementation.

The next data source 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 that section. That means that the number of rows should be the number of ToDoItem objects in your toDoItems array.

To return the number of rows in your table view

  1. In ToDoListTableViewController.m, find the tableView:numberOfRowsInSection: data source method. The template implementation looks like this:

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

    You want to return the number of to-do 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].

  2. Change the tableView:numberOfRowsInSection: data source method to return the appropriate number of rows, and remove the warning line.

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

The last data source method, tableView:cellForRowAtIndexPath:, asks for a cell to display for a given row. Each row in a table view has one cell, and that cell determines the content that appears in that row and how that content is laid out.

Until now, you’ve worked 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 your storyboard. The first task is 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 to use prototype cells

  1. In the project navigator, select Main.storyboard.

  2. In the outline view, select Table View under To-Do List Scene.

  3. With the table view selected, open the Attributes inspector image: ../Art/inspector_attributes_2x.png in the utility area.

  4. In the Attributes inspector, change the table view’s Content field 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 table view data source at runtime. The data source will load a prototype cell for each row and then configure 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. You’ll set the cell selection style 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

  1. Select the first table view cell in your table view.

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

  3. In the Attributes inspector, change the cell’s Selection field from Default to None.

  4. In the outline view, select every cell besides the first one and delete them.

    You only need one prototype cell from this point.

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:. 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 cells 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 view, you configure the cell by fetching the corresponding item in the toDoItems array and then setting the cell’s text label to that item’s name.

To display cells in your table view

  1. In the project navigator, select ToDoListTableViewController.m.

  2. Find and uncomment the tableView:cellForRowAtIndexPath: data source method. (To uncomment the method, remove the /* and */ characters surrounding it.)

    After you do that, the template implementation looks like this:

    1. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    2. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reuseIdentifier" forIndexPath:indexPath];
    3. // Configure the cell...
    4. return cell;
    5. }

    The template performs several tasks. It asks the table view for a cell with a placeholder 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 placeholder identifier to the one you set in the storyboard and then add code to configure the cell.

  3. Change the placeholder identifier to the identifier you set in your storyboard. To avoid typos, copy and paste from the storyboard to the implementation file. The line of code that returns the cell should now look like this:

    1. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ListPrototypeCell" forIndexPath:indexPath];
  4. Immediately before the return statement, add the following lines of code:

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

    These lines fetch the appropriate item in the toDoItems array and set the name of that item to display in the cell.

Your tableView:cellForRowAtIndexPath: method should look like this:

  1. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  2. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ListPrototypeCell" forIndexPath:indexPath];
  3. ToDoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
  4. cell.textLabel.text = toDoItem.itemName;
  5. return cell;
  6. }

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

Toggle Item Completion State

A to-do list isn’t much good if you can never mark items as completed. Now, you’ll add support for that. Table views allow you to implement a simple interface where the completion state of an item toggles when the user taps a cell. Specifically, a table view notifies its delegate when the user taps a cell. When the delegate receives this notification, you’ll write code to respond to it in one of two ways:

  • If the item in that cell has not been completed, then the code marks it as completed and displays the completed item with a checkmark next to it.

  • If the item in that cell has been completed, then the code marks it as uncompleted and removes the checkmark next to it.

All you have to do is implement the tableView:didSelectRowAtIndexPath: delegate method to respond to user taps and update your to-do list items accordingly.

To toggle an item as completed or uncompleted

  1. In the project navigator, select ToDoListTableViewController.m.

  2. 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. }

    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.

  3. In this method, you want to respond to a tap but not actually leave the cell selected. Add the following code to deselect the cell immediately after selection:

    1. [tableView deselectRowAtIndexPath:indexPath animated:NO];
  4. Add this code line to search for the ToDoItem in your toDoItems array that corresponds to the cell that was tapped.

    1. ToDoItem *tappedItem = [self.toDoItems objectAtIndex:indexPath.row];
  5. Toggle the completion state of the tapped item.

    1. tappedItem.completed = !tappedItem.completed;
  6. Tell the table view to reload the row whose data you just updated.

    1. [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

Your tableView:didSelectRowAtIndexPath: method should look like this:

  1. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  2. [tableView deselectRowAtIndexPath:indexPath animated:NO];
  3. ToDoItem *tappedItem = [self.toDoItems objectAtIndex:indexPath.row];
  4. tappedItem.completed = !tappedItem.completed;
  5. [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
  6. }

Checkpoint: Run your app. The list of items you added in loadInitialData is visible as cells in your table view. But when you tap items, nothing seems to happen. Why not?

The reason is that you haven’t configured the table view cell to display the completion state of an item. To do so, you need to go back to the tableView:cellForRowAtIndexPath: data source method and configure the cell to display a checkmark when an item is completed.

Table view cells can have a cell accessory on the right side. By default, there’s no accessory selected; however, you can change the cell to display a checkmark.

To display an item’s completion state

  1. Go to the tableView:cellForRowAtIndexPath: method.

  2. Add the following code just below the line that sets the text label of the cell:

    1. if (toDoItem.completed) {
    2. cell.accessoryType = UITableViewCellAccessoryCheckmark;
    3. } else {
    4. cell.accessoryType = UITableViewCellAccessoryNone;
    5. }

    This code checks the completion state of a to-do item and sets the cell accessory based on that.

Your tableView:cellForRowAtIndexPath: method should now look like this:

  1. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  2. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ListPrototypeCell" forIndexPath:indexPath];
  3. ToDoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
  4. cell.textLabel.text = toDoItem.itemName;
  5. if (toDoItem.completed) {
  6. cell.accessoryType = UITableViewCellAccessoryCheckmark;
  7. } else {
  8. cell.accessoryType = UITableViewCellAccessoryNone;
  9. }
  10. return cell;
  11. }

Checkpoint: Run your app. The list of items you added in loadInitialData is visible as cells in your table view. When you tap an item, a checkmark should appear next to it. If you tap the same item again, the checkmark disappears.

Add New Items

The final step in creating the to-do list app’s functionality is implementing the ability to add an item. When a user enters an item name in the text field in the add-to-do-item scene and taps the Save button, you want AddToDoItemViewController to create a new to-do item and pass it back to ToDoListTableViewController to display in the to-do list.

First, you need to have a to-do item to configure. Give AddToDoItemViewController a property to hold information about a new to-do item.

To add a to-do item property to AddToDoItemViewController

  1. In the project navigator, select AddToDoItemViewController.h.

    Because you’ll need to access the to-do item from your table view controller later on, it’s important to make the to-do item property public. That’s why you declare it in the interface file, AddToDoItemViewController.h, instead of in the implementation file, AddToDoItemViewController.m.

  2. In AddToDoItemViewController.h, add an import declaration to ToDoItem.h right below the #import <UIKit/UIKit.h> line.

    1. #import "ToDoItem.h"
  3. Add a toDoItem property to the interface.

    1. @interface AddToDoItemViewController : UIViewController
    2. @property ToDoItem *toDoItem;
    3. @end

To get the name of the new item, AddToDoItemViewController needs access to the contents of the text field where the user enters the name. To do this, create a connection from AddToDoItemViewController.m to the text field in your storyboard.

To connect the text field to the AddToDoItemViewController code

  1. In the project navigator, select Main.storyboard.

  2. In the outline view, select Add To-Do Item under Add To-Do Item Scene.

  3. Click the Assistant button in the Xcode toolbar to open the assistant editor.

    image: ../Art/toggle_assistant_on_2x.png

  4. Change the assistant editor from Preview to Automatic > AddToDoItemViewController.m.

  5. In your storyboard, select the text field.

  6. Control-drag from the text field on your canvas to the code display in the editor on the right, stopping the drag at the line just below the @interface line in AddToDoItemViewController.m.

    image: ../Art/outlet_textfield_drag_2x.png
  7. In the dialog that appears, for Name, type textField.

    Leave the rest of the options as they are. Your dialog should look like this:

    image: ../Art/outlet_textfield_name_2x.png
  8. Click Connect.

    Xcode adds the necessary code to AddToDoItemViewController.m to store a pointer to the text field and configures the storyboard to set up that connection.

Additionally, AddToDoItemViewController needs to know when to create a new to-do item. You want the item to be created only if the Save button was tapped. To be able to determine when this happens, add the Save button as an outlet in AddToDoItemViewController.m.

To connect the Save button to the AddToDoItemViewController code

  1. In your storyboard, select the Save button.

  2. Control-drag from the Save button on your canvas to the code display in the editor on the right, stopping the drag at the line just below your textField property in AddToDoItemViewController.m.

    image: ../Art/outlet_save_button_drag_2x.png
  3. In the dialog that appears, for Name, type saveButton.

    Leave the rest of the options as they are. Your dialog should look like this:

    image: ../Art/outlet_save_button_name_2x.png
  4. Click Connect.

You now have a way to identify the Save button. Because you want to create an item when the Save button is tapped, you need to know when that happens.

Recall that when the user taps either the Cancel or the Save button, it kicks off an unwind segue back to the to-do list—that’s something you configured in the second tutorial. Before a segue executes, the system gives the view controller involved a chance to prepare by calling prepareForSegue:. This is exactly the point at which the view controller should check which one of the buttons got tapped, and if it was the Save button, create a new to-do item. (If the Cancel button is tapped, you don’t need to do anything to save the item.)

To tell AddToDoItemViewController to create an item only when the user taps the Save button

  1. In the project navigator, select AddToDoItemViewController.m.

    If the assistant editor is still open, return to the standard editor by clicking the Standard button in the Xcode toolbar.

    image: ../Art/toggle_assistant_off_2x.png
  2. Find and uncomment the prepareForSegue: data source method. (To uncomment the method, remove the /* and */ characters surrounding it.)

    After you do that, the template implementation looks like this:

    1. #pragma mark - Navigation
    2. // In a storyboard-based application, you will often want to do a little preparation before navigation
    3. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    4. // Get the new view controller using [segue destinationViewController].
    5. // Pass the selected object to the new view controller.
    6. }
  3. In the method, see whether the Save button was tapped.

    If the Save button wasn’t tapped, instead of saving the item, you want the method to return without doing anything else, like this:

    1. if (sender != self.saveButton) return;
  4. Add this code to check whether the user entered any text in the text field.

    1. if (self.textField.text.length > 0) {
    2. }
  5. If there’s text, create a new item and give it the name of the text in the text field. Also, ensure that the completed state is set to NOfalse.

    1. self.toDoItem = [[ToDoItem alloc] init];
    2. self.toDoItem.itemName = self.textField.text;
    3. self.toDoItem.completed = NO;

    If there isn’t text, you don’t want to save the item, so you don’t need to add code to do anything else.

Your prepareForSegue: method should look like this:

  1. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  2. if (sender != self.saveButton) return;
  3. if (self.textField.text.length > 0) {
  4. self.toDoItem = [[ToDoItem alloc] init];
  5. self.toDoItem.itemName = self.textField.text;
  6. self.toDoItem.completed = NO;
  7. }
  8. }

The new item needs to be passed back to ToDoListTableViewController so that it can add the item to the to-do list. To accomplish this, you need to revisit the unwindToList: method that you wrote in the second tutorial. This method gets called after the unwind segue triggers.

The unwindToList: method takes a segue as a parameter, like all methods that are used as targets for an unwind segue. The segue parameter is the segue that unwinds from AddToDoItemViewController back to ToDoListTableViewController. Because a segue is a transition between two view controllers, it is aware of its source view controller—AddToDoItemViewController. By asking the segue object for its source view controller, you can access any data stored in the source view controller in the unwindToList: method. In this case, you want to access the toDoItem property. If it’s nil, the item was never created—either the text field had no text or the user tapped the Cancel button. If there’s a value for toDoItem, you retrieve the item, add it to your toDoItems array, and display it in the to-do list by reloading the data in the table view.

To store and display the new item

  1. In the project navigator, select ToDoListTableViewController.m.

  2. In ToDoListTableViewController.m, add an import declaration to AddToDoItemViewController.h right below the #import "ToDoItem.h" line.

    1. #import "AddToDoItemViewController.h"
  3. Find the unwindToList: method you added in the second tutorial. It looks like this:

    1. - (IBAction)unwindToList:(UIStoryboardSegue *)segue {
    2. }
  4. In this method, retrieve the source view controller—the controller you’re unwinding from, AddToDoItemViewController.

    1. AddToDoItemViewController *source = [segue sourceViewController];
  5. Retrieve the value of the controller’s toDoItem property.

    1. ToDoItem *item = source.toDoItem;

    If a user entered text in the text field of the add-to-do-item scene and then tapped the Save button, the value of the toDoItem property will contain an item. If a user didn’t enter text in the text field or tapped the Cancel button to close the screen, the value of the toDoItem property won’t contain an item. You need to check whether the item exists.

  6. Add this code to check whether the item exists.

    1. if (item != nil) {
    2. }

    If it’s nil, there’s no item in the toDoItem property, so you don’t need to do additional work to save the item.

    If it does exist, you need to add the item to your toDoItems array, like this:

    1. [self.toDoItems addObject:item];
  7. Reload the data in your table.

    Because the table view doesn’t keep track of its data, it’s the responsibility of the data source—in this case, your table view controller—to notify the table view when there’s new data for it to display.

    1. [self.tableView reloadData];

Your unwindToList: method should look like this:

  1. - (IBAction)unwindToList:(UIStoryboardSegue *)segue {
  2. AddToDoItemViewController *source = [segue sourceViewController];
  3. ToDoItem *item = source.toDoItem;
  4. if (item != nil) {
  5. [self.toDoItems addObject:item];
  6. [self.tableView reloadData];
  7. }
  8. }

Checkpoint: Run your app. Now when you click the Add button (+) and create a new item, you should see it in your to-do list. Congratulations! You’ve created an app that takes input from the user, stores it in an object, and passes that object between two view controllers. This is the foundation of moving data between scenes in a storyboard-based app.

Recap

You’re almost done with this introductory tour of developing apps for iOS. The final section gives you more information about how to find your way around the documentation, and it suggests some next steps you might take as you learn how to create more advanced apps.