Search code examples
iosuitableviewsortingswift3nsfetchedresultscontroller

How to create alphabetical section headers with NSFetchedResultsController - Swift 3


I am using NSFetchedResultsController to populate a tableView. The tableView can get quite long because it shows a list of people, and I want to sort it alphabetically. I know I need to use titleForHeaderInSection, but I am stuck on how to get the first letter of each object in my fetchedObjectsController.fetchedObjects and display that as the section header as well as sort it, just how the Contacts app works.

This is what my View Controller looks like.

var fetchedResultsController: NSFetchedResultsController<Client> = {
    let fetchRequest: NSFetchRequest<Client> = Client.fetchRequest()
    let sortDescriptors = [NSSortDescriptor(key: "name", ascending: false)]
    fetchRequest.sortDescriptors = sortDescriptors
    return NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: CoreDataStack.context, sectionNameKeyPath: "name", cacheName: nil)
}()

override func numberOfSections(in tableView: UITableView) -> Int {
    guard let sections = fetchedResultsController.sections else { return 0 }
    return sections.count
}


override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    guard let sections = fetchedResultsController.sections else { return 0 }
    return sections[section].numberOfObjects
}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "clientCell", for: indexPath)
    let client = fetchedResultsController.object(at: indexPath)
    cell.textLabel?.text = client.name

    return cell
}

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        let client = fetchedResultsController.object(at: indexPath)
        ClientController.sharedController.delete(client)
    }
}

Solution

  • This is a very small example of how you can get your headers texts, I use a class only for test that have only name, then applying map using characters.prefix we get the first characters of the names and after casting to String and sorting we have what you need

    var arrayOfUsers : [User] = [User(name:"test"),User(name:"pest"),User(name:"aest"),User(name:"nest"),User(name:"best")]
    let finalArray = arrayOfUsers.map({String.init($0.name.characters.prefix(1)) }).sorted(by: {$0 < $1})
    debugPrint(finalArray)
    

    Console log result

    ["a", "b", "n", "p", "t"]

    Hope this helps you