Search code examples
iosswiftsortingrealmrelationship

How do I sort in numerical order, the items in a to-many relationship?


I have 2 models (a "Category" model and an "Item" model) such as following. “Category” has a to-many relationship to “Item” via the property “items”

I would like to get the sorted data of “category” and “item” by its "order" but how can I do it?

  • Category.swift

    import Foundation
    import RealmSwift
    
    class Category: Object {
        @objc dynamic var name: String = ""
        @objc dynamic var order: Int = 0  // use this for sorting
        let items = List<Item>
    }
    
  • Item.swift

    import Foundation
    import RealmSwift
    
    class Item: Object {
        @objc dynamic var name: String = ""
        @objc dynamic var order: Int = 0  // use this for sotging
        let category = LinkingObjects(fromType: Category.self, property: "items")   
    }
    

I can only sort the ‘’category’’ by its order but I can’t do it with the ‘’items’’

let categories = realm.objects(Category.self).sorted(byKeyPath: "order", ascending: true)

What I want to do


Solution

  • You really aren't too far off.

    Realm Results objects can be unordered so it's important to have a strategy to keep Results objects loaded in the order you want. As is, you've got that on your category objects with this

    @objc dynamic var order: Int = 0  // use this for sorting
    

    On the other hand, Realm List objects MAINTAIN their order of insertion

    class Category: Object {
        let items = List<Item>
    

    So if chicken was added to a category items list, and then pork, that order will stick; chicken at index 0, pork at index 1.

    Meat
       chicken
       pork
    

    That's covered in the Realm Documentation

    List properties are guaranteed to preserve their order of insertion.

    Because of that, when populating a tableView as in the question, you can use the tableView titleForHeaderInSection to get the section names (Categories: Meat, Vegetables, Fruit) from the results object (because they were ordered when the results were loaded from Realm)

    Then for the rows within each section, you can read the Category->items List for the items, which will be ordered by their insertion because they are in a List.

    For completeness, here's the code for handling the section of a tableView

    //
    //handle sections
    //
    func numberOfSections(in tableView: UITableView) -> Int {
        return self.yourCategoryResults.count
    }
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        let title = self.yourCategoryResults[section].name //the section title
        return title
    }
    

    and then the code to handle the rows in each section

    //
    //handleTableView rows
    //
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let rowsInSection = self.yourCategoryResults[section].items.count
        return rowsInSection
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellReuseIdentifier", for: indexPath)
        let item = self.yourCategoryResults[indexPath.section].items[indexPath.row]
        let text = item.name
        cell.textLabel?.text = text
        return cell
    }