This is the perfect opportunity to use a NSFetchResultsController
in combination with your UITableViewController
.
Assuming your model has something like this:

Than your code would look like this:
import UIKit
import CoreData
class ViewController: UITableViewController {
lazy var fetchedResultsController: NSFetchedResultsController<Course> = {
let fetchRequest = NSFetchRequest<Course>(entityName: "Course")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "program.name", ascending: true)]
let moc = *your context*
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: moc, sectionNameKeyPath: "program.name", cacheName: nil)
// controller.delegate = self
return controller
}()
override func numberOfSections(in tableView: UITableView) -> Int {
guard let sections = self.fetchedResultsController.sections else {
return 0
}
return sections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let sections = self.fetchedResultsController.sections else {
return 0
}
return sections[section].numberOfObjects
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
guard let sections = self.fetchedResultsController.sections else {
return ""
}
return sections[section].indexTitle
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CourseCell") as! UITableViewCell
cell.course = self.fetchedResultsController.object(at: indexPath)
return cell
}
}
When you provide the sectionNameKeyPath to the fetched results controller it will automatically 'group' the results. You could further limit the results by using a predicate on the controller.