Core Data from Scratch: More NSFetchedResultsController

In this tutorial, we continue our exploration of the NSFetchedResultsController class by adding the ability to update and delete to-do items. You'll notice that updating and deleting to-do items is surprisingly easy thanks to the groundwork we laid in the previous tutorial.

1. Updating a Record's Name

Step 1: Create View Controller

Start by creating a new UIViewController subclass named TSPUpdateToDoViewController. In TSPUpdateToDoViewController.h, declare an outlet, textField of type UITextField, and two properties, managedObjectContext of type NSManagedObjectContext and record of type NSManagedObject. Add an import statement for the Core Data framework at the top.

In the view controller's implementation file, TSPUpdateToDoViewController.m, create two actions, cancel: and save:. Their implementations can remain empty for the time being.

Step 2: Update Storyboard

Open the main storyboard, Main.storyboard, add a new view controller object, and set its class to TSPUpdateToDoViewController in the Identity Inspector. Create a manual segue from the TSPViewController class to the TSPUpdateToDoViewController class. In the Attributes Inspector, set the segue's style to push and its identifier to updateToDoViewController.

Add a UITextField object to the view of the TSPUpdateToDoViewController object and configure it just like we did with the text field of the TSPAddToDoViewController class. Don't forget to connect the view controller's outlet with the text field.

As in the TSPAddToDoViewController class, add two bar button items to the view controller's navigation bar, set their identities to Cancel and Save respectively, and connect each bar button item to the corresponding action in the Connections Inspector.

Step 3: Passing a Reference

We also need to make a few changes to the TSPViewController class. Add an import statement for the TSPUpdateToDoViewController class at the top and declare a property named selection of type NSIndexPath to the private class extension in TSPViewController.m.

Next, implement the tableView:didSelectRowAtIndexPath: method of the UITableViewDelegate protocol. In this method, we temporarily store the user's selection in the selection property.

In the class's prepareForSegue:sender:, we fetch the record that corresponds with the user's selection and pass it to the TSPUpdateToDoViewController instance. To prevent any unexpected behavior, we only perform this step if selection isn't nil and reset the selection property after fetching the record from the fetched results controller.

Step 4: Populating the Text Field

In the viewDidLoad method of the TSPUpdateToDoViewController class, populate the text field with the name of the record as shown below.

Step 5: Updating the Record

In the cancel: action, we pop the update view controller from the navigation controller's navigation stack.

In the save: action, we first check if the text field is empty and show an alert view if it is. If the text field contains a valid value, we update the record's name attribute and pop the view controller from the navigation controller's navigation stack.

This is all it takes to update a record using Core Data. Run the application once more and see if everything is working. The fetched results controller automatically detects the change and notifies its delegate, the TSPViewController instance. The TSPViewController object, on its turn, updates the table view to reflect the change. It's that easy.

2. Updating a Record's State

Step 1: Updating TSPToDoCell

When a user taps the button on the right of a TSPToDoCell, the item's state needs to change. To accomplish this, we first need to update the TSPToDoCell class. Open TSPToDoCell.m and add a typedef for a block named TSPToDoCellDidTapButtonBlock. Next, declare a property of type TSPToDoCellDidTapButtonBlock and make sure the property is copied on assignment.

Head to the class's implementation file, TSPToDoCell.m, and invoke setupView, a helper method, in awakeFromNib.

In setupView, we configure the doneButton object by setting images for each state of the button and adding the table view cell as a target. When the user taps the button, the table view cell is sent a message of didTapButton: in which we invoke the didTapButtonBlock block. You'll see in a moment how convenient this pattern is. The images are included in the source files of this tutorial, which you can find on GitHub.

Step 2: Updating TSPViewController

Thanks to the NSFetchedResultsController class and the foundation we've laid, we only need to update the configureCell:atIndexPath: method in the TSPViewController class.

Step 3: Saving Changes

You may be wondering why we aren't saving the managed object context. Won't we lose the changes we've made if we don't commit the changes to the persistent store? Yes and no.

It is true that we need to write the changes of the managed object context to the backing store at some point. If we don't, the user will lose some of its data. However, there is no need to save the changes of a managed object context every time we make a change.

A better approach is to save the managed object context the moment the application moves to the background. We can do this in the applicationDidEnterBackground: method of the UIApplicationDelegate protocol. Open TSPAppDelegate.m and implement applicationDidEnterBackground: as shown below.

However, this doesn't work if the application is force quit by the user. It's therefore a good idea to also save the managed object context when the application is terminated. The applicationWillTerminate: method is another method of the UIApplicationDelegate protocol that notifies the application't delegate when the application is about to be terminated.

Note that we have duplicate code in applicationDidEnterBackground: and applicationWillTerminate:. It's therefore a good idea to create a helper method to save the managed object context and call this helper method in both delegate methods.

3. Deleting Records

You'll be surprised by how easy it is to delete records using the NSFetchedResultsController class. Start by implementing the tableView:canEditRowAtIndexPath: method of the UITableViewDataSource protocol.

The second method of the UITableViewDataSource protocol that we need to implement is tableView:commitEditingStyle:forRowAtIndexPath:. In this method we fetch the managed object the user has selected for deletion and pass it to the deleteObject: method of the managed object context of the fetched results controller.

Because we've already implemented the NSFetchedResultsControllerDelegate protocol, the user interface is automatically updated, animations included.

Conclusion

I hope you agree that the NSFetchedResultsController class is a very convenient member of the Core Data framework. If you understand the basics of the Core Data framework, then it's not difficult to get up to speed with this class. I encourage you to further explore its API to find out what else it can do for you.

Tags:

Comments

Related Articles