Swift Core Data From Scratch: Workshop 2

Today, you will implement these tasks in the SwiftCoreDataFromScratch iOS project you worked on last week:

  • Fetch managed objects from the database
  • Display fetched managed objects in the Books View’s table view cells
  • Pass a managed object to the Add Book class

Before You Begin

Now, before you get started in implementing above tasks in the Swift Core Data From Scratch project, do these tasks first:

  1. Use the Source Control menu to create a new branch from the insert-managed-object branch. Give it the name “fetch-and-display-managed-objects”.
  2. Import the CoreData module in the BookViewController.swift file.
  3. Conform the class to the NSFetchedResultsControllerDelegate protocol.
  4. Create a constant called context and initialize it with the AppDelegate’s managedObjectContext variable.

Once you’ve performed task 2-4, the top portion of the BooksViewController class should look like this now:

import UIKit
import CoreData
class BooksViewController: UIViewController, NSFetchedResultsControllerDelegate {
  ...
  var fetchedResultsController: NSFetchedResultsController!
  let context = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext!
}

In addition to performing above tasks, I want you to create a segue that connect the Books View scene’s Table View Cell to the Add Book View scene. Next, enter “idEditBook” in the Identifier attribute field.

scdfs-figure3-4

Fetch Managed Objects

Since you’ll be using the NSFetchedResultsController class and the AppDelegate’s managedObjectObjectContext, you declare variables for them in the BooksViewController class. Now, to fetch all managed objects from the app’s database file and store them in the fetchedResultsController object variable, you’ll have to implement this function in the BooksViewController.swift file. By the way, the fetchedResultsController variable holds an array of managed objects.

func fetchManagedObjects() {
    // Setup the fetch request with a sortDescriptor for the fetchedResultController
    let fetchRequest = NSFetchRequest(entityName: "Book")

    fetchRequest.fetchLimit = 25

    let sortDescriptor = NSSortDescriptor(key: "bookTitle", ascending: true)
    fetchRequest.sortDescriptors = [sortDescriptor]

    // Pass the fetchRequest and the context as parameters to the fetchedResultController
    fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext:context,
        sectionNameKeyPath: nil, cacheName: nil)

    // Make the fetchedResultsController a delegate of this class
    fetchedResultsController.delegate = self

    // Execute the fetch request or display an error message in the Debugger console
    var error: NSError? = nil
    if (!fetchedResultsController.performFetch(&error)) {
        println("Error: \(error?.localizedDescription)")
    }
}

Next, call the fetchManageObjects() function in the viewDidLoad() function like this:

// Fetch all managed objects (book records) from the app's database file
  fetchManagedObjects()

Display Fetched Managed Objects

Now, to display fetched managed objects in the Books View’s table view cells, add code shown below in the first three table view data source functions.

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    // Return the number of sections in the table view
    return fetchedResultsController.sections!.count
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // Return the number of rows in the section
    let sectionInfo = fetchedResultsController.sections![section] as NSFetchedResultsSectionInfo
    return sectionInfo.numberOfObjects
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell

    // Put a managed object in the book object 
    let book = fetchedResultsController.objectAtIndexPath(indexPath) as Book

    // Configure the table view cell and return it
    var stringObject = book.photoUrl as String!
    cell.imageView!.image = AddViewController.getImage(stringObject)
    cell.textLabel!.text = book.bookTitle
    cell.detailTextLabel!.text = book.authorName
    return cell
}

Modify The AddViewController Class Code

Take a look at the statement on code line 20, in the cellForRowAtIndexPath() function. As you can see, the getImage() function was used to set the imageView’s image property. To prevent the compiler from issuing fatal errors, you’ll have to modify code in the AddViewController.swift class file.

Start by locating the getImage() function and add the keyword class, to the getImage() function’s header declaration line like this:

class func getImage(urlString: String) -> UIImage {

By append the keyword, “class” to the function’s name, you can now use the function in all the project’s class files.

Go to the touchesBegan() function and change the last statement to this:

imageView.image = AddViewController.getImage(photoUrl.text)

Thats all the modifications you have to make in the AddViewController.swift file.

Output

Now that you’ve added code in the BooksViewController.swift and modified the AddViewController.swift file’s code, run the app in the iPhone 5s Simulator and you’ll see this output:

scdfs-figure3-1

Pass a Managed Object to The Add Book Class

When you click a cell in the Books View’s table view cell, the app should pass the cell’s managed object to the AddBookViewController class. To do that, add this object variable declaration statement in the AddBookViewController.swift file:

class AddBookViewController: UIViewController {
  ...
  var bookObject: Book!
  ...
}

Switch back to the BooksViewController.swift file, uncomment the prepareForSegue() function, and add code shown in the image below in the function. Next, use the Source Control menu to commit changes you’ve made to the project, in the git repository.

scdfs-figure3-2

When you run the app in the iPhone 5s Simulator and tap a cell in the Books View. The app display the Add Book View and the println() function” messages in Xcode’s Debugger console-see above image.

You’ve reached the end of workshop 2. Next week, I will bring you workshop 3. In the mean time, comments are welcomed! 🙂

  • Dennis

    This is a great tutorial but i have run into a problem! 🙂 everything is good up until the bit
    “OUPUT heading: Now that you’ve added code in the BooksViewController.swift and modified the…”and then it wont compile.

    I get the following error:
    “2015-04-04 10:35:46.511 SwiftCoreDataFromScratch[2204:448692] *** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘NSFetchRequest could not locate an NSEntityDescription for entity name ‘Movie”

    I’m a bit confused about why the entity is called “Movie” in the code. I thought this was maybe a mistake so I changed it to “Book” but that still didnt compile either (when I changed it to “Book” I got the following compile error):
    “SwiftCoreDataFromScratch[2284:486866] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘keypath name not found in entity ‘”

    I’ve gone over it multiple times to make sure I have everything you specified and I think I have (but maybe I dont?) 🙂 Can you help? Or maybe you have the source code from the end of the 2nd workshop so I can use that to continue with your workshops?

    Thanks again, and great work on the workshop!
    Dennis

    • Hey Dennis!

      The reason why you are getting above mentioned errors is because the “entityName:” on code line 03 should be Book; not Movie. Also, the “key:” on code line 07 should be bookTitle; not name. I updated the FETCHED MANAGED OBJECT section’s source code; so the app should run now.

      Thank you for pointing out these error Dennis.