Implementing Multiple Table Views with NSFetchedResultsController in iOS Core Data

Introduction to Core Data and NSFetchedResultsController

Core Data is a framework in iOS, macOS, watchOS, and tvOS that provides a robust data modeling system for managing data in your applications. It abstracts away many details of working with databases, allowing you to focus on the logic of your application’s data management.

At its core (pun intended), Core Data is built around three main components: models, managed objects, and persistence stores. Models represent the structure of your data, managed objects are instances of classes that conform to a specific protocol, and persistence stores manage where data is stored on disk or in memory.

NSFetchedResultsController is a powerful tool for managing fetched results in Core Data. It provides a simple way to notify your table view controller when new data has been added, removed, or changed, allowing you to update your display without manually polling the data.

One-to-Many Tableviews with Core Data

In this article, we’ll explore how to implement an app with multiple table views that use NSFetchedResultsController for managing fetched results in both parent and child parts of a relationship.

Setting Up Your Core Data Stack

Before we dive into implementing our app’s features, let’s set up our Core Data stack. First, create a new Xcode project and select “Single View App” under the iOS section. Then, click on the “Add” button next to the “Core Data Model” section in the Project Navigator.

This will create a new file called DataModel.xcdatamodeld in the Project Navigator. Open this file and add two entities: Parent and Child. The Parent entity has a single attribute called name, while the Child entity has a single attribute called description.

Creating Table View Controllers

Next, create a new table view controller for each entity:

  • For the Parent entity, create a new file called ParentViewController.swift.
  • For the Child entity, create a new file called ChildViewController.swift.

ParentViewController.swift

import UIKit

class ParentViewController: UITableViewController {

    // MARK: - Properties
    
    let fetchRequest: NSFetchedResultsController<Parent> = {
        let fetchRequest = NSFetchedResultsController(fetchRequest: NSFetchRequest<Parent>(entityName: "Parent"),
            managedObjectContext: (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext,
            sectionNameKeyPath: nil,
            cacheNameKeyPath: nil)

        fetchRequest.delegate = self

        return fetchRequest
    }()
    
    // MARK: - viewDidLoad
    
    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Parents"
        
        navigationController?.navigationBar.titleDisplayMode = .plain
        
        tableView.dataSource = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "ParentCell")
        tableView.register(RefreshControl.self)
        
        fetchRequest.fetchDelegate = self
        
    }
    
    // MARK: - NSFetchedResultsControllerDelegate
    
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        print("Controller did change content")
    }

}

ChildViewController.swift

import UIKit

class ChildViewController: UITableViewController {

    // MARK: - Properties
    
    let fetchRequest: NSFetchedResultsController<Child> = {
        let fetchRequest = NSFetchedResultsController(fetchRequest: NSFetchRequest<Child>(entityName: "Child"),
            managedObjectContext: (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext,
            sectionNameKeyPath: nil,
            cacheNameKeyPath: nil)

        fetchRequest.delegate = self

        return fetchRequest
    }()
    
    // MARK: - viewDidLoad
    
    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Children"
        
        navigationController?.navigationBar.titleDisplayMode = .plain
        
        tableView.dataSource = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "ChildCell")
        tableView.register(RefreshControl.self)
        
        fetchRequest.fetchDelegate = self
        
    }
    
    // MARK: - NSFetchedResultsControllerDelegate
    
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        print("Controller did change content")
    }

}

Integrating NSFetchedResultsController

Next, we need to integrate NSFetchedResultsController into our table view controllers.

ChildViewController Integration

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "ChildCell", for: indexPath)
    
    if let child = fetchRequest.fetchedObjects?[indexPath.row] as? Child {
        cell.textLabel?.text = child.description
    }
    
    return cell
}

ParentViewController Integration

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "ParentCell", for: indexPath)
    
    if let parent = fetchRequest.fetchedObjects?[indexPath.row] as? Parent {
        cell.textLabel?.text = parent.name
        
        // Create a child view controller to display the children
        let childViewController = ChildViewController()
        
        // Push the child view controller onto the navigation stack
        self.navigationController?.pushViewController(childViewController, animated: true)
    }
    
    return cell
}

Conclusion

In this article, we explored how to implement an app with multiple table views that use NSFetchedResultsController for managing fetched results in both parent and child parts of a relationship. We covered the basics of Core Data, set up our data model, created table view controllers, and integrated NSFetchedResultsController into our table view controllers.

By following this guide, you should be able to create your own app with multiple table views that use NSFetchedResultsController for managing fetched results.


Last modified on 2024-04-21