1

I am new to the Swift and the SwiftUI. Trying to understand how sorting works on SwiftUI Table view.

The following code does what I intend to do, but I am not able to understand how does it actually work with multiple key path. Need help with the answers for the questions posted below the code.

// MARK: - Student Model
struct Student: Codable, Identifiable {
    let id: String
    let name: String
    let gradeHistory: GradeHistory

    enum CodingKeys: String, CodingKey {
        case id, name
        case gradeHistory = "grade_history"
    }
}

// MARK: - GradeHistory Model
struct GradeHistory: Codable, Identifiable{
    let id: String?
    let semester: String
    let subjects: Subjects

    init(
        id: String? = UUID().uuidString,
        semester: String,
        subjects: Subjects
    ) {
        self.id = id ?? UUID().uuidString
        self.semester = semester
        self.subjects = subjects
    }
}

// MARK: - Subjects Model
struct Subjects: Codable, Identifiable {
    let id: String?
    let math: Int
    let science: Int
    let english: Int
    let physics: Int
    let computer: Int
    let socialScience: Int

    init(
        id: String? = nil,
        math: Int,
        science: Int,
        english: Int,
        physics: Int,
        computer: Int,
        socialScience: Int
    ) {
        self.id = id ?? UUID().uuidString
        self.math = math
        self.science = science
        self.english = english
        self.physics = physics
        self.computer = computer
        self.socialScience = socialScience
    }

    enum CodingKeys: String, CodingKey {
        case id = "id"
        case math = "Math"
        case science = "Science"
        case english = "English"
        case physics = "Physics"
        case computer = "Computer"
        case socialScience = "Social Science"
    }
}

let _students: [Student] = [
    Student(
        id: "S001",
        name: "John Doe",
        gradeHistory: GradeHistory(
            semester: "Fall 2022",
            subjects: Subjects(
                math: 85,
                science: 78,
                english: 90,
                physics: 88,
                computer: 95,
                socialScience: 80
            )
        )
    ),
    Student(
        id: "S002",
        name: "Jane Smith",
        gradeHistory: GradeHistory(
            semester: "Spring 2023",
            subjects: Subjects(
                math: 92,
                science: 85,
                english: 89,
                physics: 91,
                computer: 94,
                socialScience: 88
            )
        )
    ),
    Student(
        id: "S003",
        name: "Emily Johnson",
        gradeHistory: GradeHistory(
            semester: "Fall 2023",
            subjects: Subjects(
                math: 78,
                science: 81,
                english: 82,
                physics: 86,
                computer: 90,
                socialScience: 84
            )
        )
    )
]


struct StudentGradeHistoryView: View {
    @State var students = _students

    @State private var sortOrder = [KeyPathComparator(\Student.name)]

    var body: some View {
        NavigationStack {
            Table(students,
                  selection: students.selectedStudents,
                  sortOrder: $sortOrder) {
                TableColumn("Index") { student in
                    let index = (students.firstIndex(
                        where: { $0.id == student
                            .id }) ?? 0)
                    Text("No. \(index + 1)")
                }

                TableColumn("Id", value: \.id)

                TableColumn("Name", value: \.name)
                    .width(min: 150)

                TableColumn("Math") { student in
                    Text("\(student.gradeHistory.subjects.math)")
                        .foregroundStyle(
                            gradeColor(for: student.gradeHistory.subjects.math)
                        )
                }
                .defaultVisibility(.automatic)
                TableColumn("Science") { student in
                    Text("\(student.gradeHistory.subjects.science)")
                        .foregroundStyle(gradeColor(for: student.gradeHistory.subjects.science))
                }
                TableColumn("English") { student in
                    Text("\(student.gradeHistory.subjects.english)")
                        .foregroundStyle(gradeColor(for: student.gradeHistory.subjects.english))
                }
                TableColumn("Physics") { student in
                    Text("\(student.gradeHistory.subjects.physics)")
                        .foregroundStyle(gradeColor(for: student.gradeHistory.subjects.physics))
                }
                TableColumn("Computer") { student in
                    Text("\(student.gradeHistory.subjects.computer)")
                        .foregroundStyle(gradeColor(for: student.gradeHistory.subjects.computer))
                }
                TableColumn("Social Science") { student in
                    Text("\(student.gradeHistory.subjects.socialScience)")
                        .foregroundStyle(gradeColor(for: student.gradeHistory.subjects.socialScience))
                }
            }
            .onChange(of: sortOrder) {
                students.sort(using: sortOrder)
            }
        }
    }
}

My question is how I can use KeyPathComparator in this model to sort data with multiple comparator paths like

    @State private var sortOrder = [
        KeyPathComparator(\Student.name),
        KeyPathComparator(\Subjects.math)
    ]

It's not working if i user this sortOrder.

I was expectin that each table column should be sortable but i don't know why different KeyPathCoparator is not working.

4
  • 2
    The entity to sort is Student. So you can sort by Student properties. But when suggesting Subjects, the link between Student and Subjects is not direct, no? You need to go through GradeHistory, then Subjects then maths. I guess, then that \Student.gradeHistory.subjects.math or something like that should be the path, no? Commented Oct 16, 2024 at 7:25
  • 2
    Or to put it in another way, you can only sort on the paths you use in your TableColumn declarations. So Text("\(student.gradeHistory.subjects.math)") becomes KeyPathComparator(\Student.gradeHistory.subjects.math) Commented Oct 16, 2024 at 7:31
  • @JoakimDanielson I have tried that, but it's not working don't know why, is there any issue with Type as maths having Double type, it's compiling without error but sorting is not working Commented Oct 16, 2024 at 8:58
  • Also note that your code doesn't compile Commented Oct 16, 2024 at 10:52

1 Answer 1

2

The problem is that you are not using an init for TableColumn that supports sorting, see the documentation for all alternatives but here is one example of making the math column sortable

TableColumn("Math Sortable", value: \.gradeHistory.subjects.math) {
    Text("\($0.gradeHistory.subjects.math)")
}

and with that you can now get correct sorting from the start

@State private var sortOrder = [
    KeyPathComparator(\Student.name),
    KeyPathComparator(\Student.gradeHistory.subjects.math)
]
Sign up to request clarification or add additional context in comments.

2 Comments

It's Mandatory to provide the value key-path even when we are having full object in that closer to make sorting work right?, also it's irrespective of value Type like String, Float, or Bool?
No there are many ways to write the declaration of sortOrder if that is what you refers to. And yes the type isn't relevant for that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.