TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

keyPath issues

Forums > macOS

In a Document app for MacOS using latests non-beta tools.

This works:

   Table(transactions) {
   TableColumn("Payee") { transaction in
          Text("\(transaction.payee ?? "no payee")")

But this column version does not:

   Table(transactions) {TableColunn("Payee", value: \.payee)

The error is about the .payee keyPath "Key path value type 'String?' cannot be converted to contextual type 'String'"

payee is an optional property of the @Model class TheTransaction. The class has an init and also code to make it codeable (for JSON import).

If the model is in a standard struct there is no problem and both work, although the former is not not sortable.

The goal is to list all the transactions in a Table, sortable by any column. Straighforward when not using SwiftData.

Anyone have an example of presenting SwiftData in a Table and eligible for sorting?



You could add a computed property to your Model unwrapping the optional and use that as your keyPath,

var payee: String?

var unwrappedPayee: String {
    payee ?? "no payee"
TableColumn("Payee", value: \.unwrappedPayee)


Yes that does compile, but isn't it just the same as making the var non-optional (which also works).


Sorry i misunderstood your question; The only way i've been able to display and sort a Table from SwiftData is using a computed property to sort after the query and displaying the sorted version in the table, here's an example using a Model i took from apple:

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext
    @Query private var people: [Person]

    @State var sortOrder = [KeyPathComparator(\Person.givenName)]
    @State var id = UUID()

    var sortedPeople: [Person] {
        people.sorted(using: sortOrder)

    var body: some View {
        NavigationStack {
            Table(sortedPeople, sortOrder: $sortOrder) {
                TableColumn("Given Name", value: \.givenName)
                TableColumn("Family Name", value: \.familyName)
                TableColumn("E-Mail Address", value: \.emailAddress)
            .onChange(of: sortOrder) {
                id = UUID()
            .toolbar(content: {
                ToolbarItem {
                    Button("Add Example") {
                        try? modelContext.delete(model: Person.self)

                        let person1 = Person(givenName: "Juan", familyName: "Chavez", emailAddress: "")

                        let person2 = Person(givenName: "Mei", familyName: "Chen", emailAddress: "")

                        let person3 = Person(givenName: "Tom", familyName: "Clark", emailAddress: "")

                        let person4 = Person(givenName: "Gita", familyName: "Kumar", emailAddress: "")

class Person: Identifiable {
    let givenName: String
    let familyName: String
    let emailAddress: String
    let id = UUID()

    var fullName: String { givenName + " " + familyName }

    init(givenName: String, familyName: String, emailAddress: String) {
        self.givenName = givenName
        self.familyName = familyName
        self.emailAddress = emailAddress


Updated the code inside the sortedPeople to use .sorted(using: ) instead of the mess of a switch.


Hacking with Swift is sponsored by Superwall.

SPONSORED Superwall lets you build & test paywalls without shipping updates. Run experiments, offer sales, segment users, update locked features and more at the click of button. Best part? It's FREE for up to 250 conversions / mo and the Superwall team builds out 100% custom paywalls – free of charge.

Learn More

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.