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 calledParentViewController.swift
. - For the
Child
entity, create a new file calledChildViewController.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