SwiftUI Table sorts only the first column if the data is loaded later

All examples regarding SwiftUI Tables and sorting work fine, but they also have all the data available immediately. If I load the data in .task, sorting only works on the first column.

Test data:


struct Customer: Identifiable {
    let id = UUID()
    let name: String
    let email: String
    let creationDate: Date
}

func parseDate(from text: String) -> Date {
    let formatter = DateFormatter()
    formatter.dateFormat = "dd/MM/yyyy"
    return formatter.date(from: text) ?? Date()
}

func getTestData() -> [Customer] {
    return [
        Customer(name: "John Smith",
                 email: "john.smith@example.com",
                 creationDate: parseDate(from: "04/11/2015")),
        Customer(name: "Jane Doe",
                 email: "jane.doe@example.com",
                 creationDate: parseDate(from: "29/04/2009")),
        Customer(name: "Bob Johnson",
                 email: "bob.johnson@example.com",
                 creationDate: parseDate(from: "01/08/2010"))]
}

And here the view:

  • table 1 populates the array in the initializer
  • table 2 loads the content in .task, initially it is empty
  • table 3 loads the content in .task, initially it contains one dummy object
struct SortableTable: View {
    @State var customers1 = getTestData()
    @State var customers2: [Customer] = []
    @State var customers3: [Customer] = [Customer(name: "", email: "", creationDate: Date())]
    
    @State private var sortOrder1 = [KeyPathComparator(\Customer.name)]
    @State private var sortOrder2 = [KeyPathComparator(\Customer.name)]
    @State private var sortOrder3 = [KeyPathComparator(\Customer.name)]
    
    var body: some View {
        Table(customers1, sortOrder: $sortOrder1) {
            TableColumn("name", value: \.name)
            TableColumn("email", value: \.email)
            
            TableColumn("joined at", value: \.creationDate) { customer in
                Text(customer.creationDate, style: .date)
            }
        }
        .frame(height: 200)
        .onChange(of: sortOrder1) { oldOrder, newOrder in
            customers1.sort(using: newOrder)
        }
        
        Table(customers2, sortOrder: $sortOrder2) {
            TableColumn("name", value: \.name)
            TableColumn("email", value: \.email)
            
            TableColumn("joined at", value: \.creationDate) { customer in
                Text(customer.creationDate, style: .date)
            }
        }
        .frame(height: 200)
        .onChange(of: sortOrder2) { oldOrder, newOrder in
            customers2.sort(using: newOrder)
        }
        .task {
            customers2 = getTestData()
        }
        
        Table(customers3, sortOrder: $sortOrder3) {
            TableColumn("name", value: \.name)
            TableColumn("email", value: \.email)
            
            TableColumn("joined at", value: \.creationDate) { customer in
                Text(customer.creationDate, style: .date)
            }
        }
        .frame(height: 200)
        .onChange(of: sortOrder3) { oldOrder, newOrder in
            customers3.sort(using: newOrder)
        }
        .task {
            customers3 = getTestData()
        }
        
        Spacer()
    }
}

The result is that table 1 and table 3 can be sorted by all columns, while table 2 can only be sorted (for whatever reason) be the first column:

HIH and let me know, if I misunderstood something.

Wow, I couldn't figure out why my Table sorting was not working. Your post helped me understand the cause - loading the data asynchronously. Also, thanks for the workaround - using non-empty array to start. I think this is definitely a bug in SwiftUI Table, but I'm grateful for the workaround.

SwiftUI Table sorts only the first column if the data is loaded later
 
 
Q