Core Data for iOS: Part 2 – Fetching and creating objects

This article builds upon the last week’s blog post on Core Data. In the Part 1 of the series I explained what the Core Data stack is and how to create a data model for a simple application. This article goes one step further and shows how to extract and display the data from the persistent store and create or delete records.

Introducing NSFetchResultController

Core Data may not be a database strictly speaking, but the objects it handles are nonetheless structured in a similar way to records in database tables. Each object can be considered a record, its attributes are equivalent to table fields and the relationships with other Core Data objects are also appear as properties.

It is thus natural to display same type of Core Data objects in a UITableView. To make things easier for the developer, Apple provides the NSFetchedResultsController class, which acts like the data source for the table view. The main benefits of using a fetched results controller are the ability to inform the table view about the updates that occur in the data store and the caching feature that improves the data polling performance. It also allows to write a lot less code than if we had to handle the data polling manually.

UITableView for Core Data objects

The project sample for this article can be downloaded on GitHub. It reuses the Publisher – Book – Author data model built for the previous article. The project bundle contains a pre-populated SQLite file that will be used as the persistent store for the Core Data stack.

Our first goal is to display the data using table views and NSFetchedResultsController. Because the Core Data entities are linked to each other to form a data tree, we can embed the table views in a navigation controller and browse the data one level at a time. On the main screen we will display the list of Publishers and, for each Publisher, we will show on a second screen the list of books they published. We will use NSFetchedResultsController to extract the data and display it both table views.

Open Xcode and create a single view application with support for Core Data:

xcode project with core data support

Remove from the project file tree the source files we don’t need: ViewController.h and ViewController.m. Also delete the .xcdatamodeld file automatically created by Xcode and I add instead the data model definition file from the previous blog post. If you create the project from scratch, feel free to replace the default data model file with the one you copy from the sample project.

Open the Main.storyboard file and delete the default view controller, then drag and drop an UITableViewController from the Object library. Select the table view controller, open the Editor menu and click on Embed In / Navigation Controller option. Xcode adds a navigation controller to the storyboard and initializes its root view controller with the table view controller we previously added. Finally, select the navigation controller and make it the Initial view controller in the Attributes inspector:

publishers table view controller embedded in navigation controller

We need custom UITableViewController sub-classes for the tableviews. Create the PublishersTableViewController class which inherits from UITableViewController; remove the commented code from the default implementation file and associate the class to the table view controller in the storyboard:

custom table view controller sub-class

The data source for the publishers table view will be a NSFetchedResultsController that polls the information from the Core Data managed context and provides it to the table view.

The NSFetchedResultsController instance is initialized with a NSFetchRequest returning an array of Publisher managed objects.
The data can be filtered using an NSPredicate, but we want all the publishers to be displayed in the table view, so we will not use any predicate.
NSFetchedResultsController is able to sort the data by one or more entity attributes before providing it to the table view; for that, it uses the array of NSSortDescriptor objects passed to the sortDescriptors property. In our case, the publishers are sorted by their names.
The data can also be grouped by the values of a specific attribute of the fetched entity; the name of the attribute is provided in the sectionNameKeyPath argument of the NSFetchedResultsController initializer. The distinct values of the key path are subsequently used as the table view section names. In the project sample we don’t separate the publishers data in sections, but we will see how to implement this particular feature in the Part 3 of the Core Data series.

We use lazy instantiation to create the NSFetchedResultsController object; it is initialized using the public managedObjectContext property of the application delegate:

The communication between the NSFetchedResultsController and the table view is established through the data source protocol methods -numberOfSectionsInTableView:, -tableView:numberOfRowsInSection: and -tableView:cellForRowAtIndexPath::

Don’t forget to check in the Storyboard the reuse identifier for the table view cell prototype is set to “Cell” in the Attributes inspector.

If you run the app, the initial table view should be populated with the names of the Publishers sorted in alphabetical order.

Add a second table view controller to the Storyboard to display the books published by a selected publisher and, for each book, the author name as a subtitle.
For the Books table view, I also used NSFetchedResultsController to poll the Book managed objects filtered by publisher (check out the CCRBooksTableViewController.m to see how to use NSPredicate to filter the fetched data):

custom table view controller sub-class

Create and delete Core Data objects

In this section we will see how to create Book managed objects and insert them into the Core Data persistent store.

As you could see in the previous screenshot, there is a + button on the navigation bar of the Books table view. If you press this button, a modal view controller is displayed allowing to enter the book title, the number of pages and the publishing date.

When we tap the Save button, a Book managed object is created and saved in the persistent store (provided the title is not empty).

To create a managed object for a specific entity, we use the NSManagedObject -initWithEntity:insertIntoManagedObjectContext: initializer. We then assign values to the managed object attributes, and, finally, we save the managed context, which add the new managed object to the persistent store:

The -saveContext method is conveniently added to the AppDelegate class when we create a Core Data project.

To delete a Book managed object, we use the NSManagedObjectContext -deleteObject: method, then we save the managed context to propagate the deletion to the persistent store.

In the sample project, I make use of the -tableView:editActionsForRowAtIndexPath: method of the UITableViewDelegate protocol to add a Delete button revealed by swiping left on a table view cell. This method is available starting iOS8.
For the swipe gesture to work, we also have implement the -tableView:commitEditingStyle:forRowAtIndexPath: and -tableView:canEditRowAtIndexPath: delegate methods.

Reacting to changes in the managed context

NSFetchedResultsController API comes with a delegate protocol which exposes a few very useful methods. They are generally implemented in the table view controller in order to update the user interface when changes occur in the managed object context attached to the fetched results controller.

The different types of changes we have to handle are:

  • new managed objects are created: insert rows into the table view
  • managed objects are deleted: remove the corresponding rows from the table view
  • managed objects are updated: refresh the information displayed in the table view
  • managed objects are moved to other positions: remove the rows from the initial positions in the table view and add them to the new positions

Here are the NSFetchedResultsControllerDelegate methods we implemented in the Books table view controller to update the display when a Book is created or deleted:

Conclusion

Through some rather simple examples presented in this article, we explored how to display the information stored in Core Data using the powerful NSFetchedResultsController class, and how to create and delete managed objects.

In the 3rd part of the series, we will see how to handle the changes that can occur in the data model throughout the app’s lifecycle and examine some specific Core Data features like transient properties and managed object sub-classes.

 

Catalin Rosioru

 

2 thoughts on “Core Data for iOS: Part 2 – Fetching and creating objects

Comments are closed.