Using Xcode Version 15.0 beta (15A5160n) steps to reproduce:
Insert the code below into a new project
Build and run
First time, tap the “Setup” button to populate the SwiftData container.
All of the Attribute objects will be shown together sorted by
‘Attribute.order’
Comment out the @Query with the single SortDescriptor and then uncomment the @Query with two SortDescriptor's.
Run the project again and it will crash with the following console message:
SwiftData/DataUtilities.swift:1179: Fatal error: Unexpected type for Expansion: Item
The expectation was for the Attributes to be listed in order within groups for each Item object, also in order by their Item.order property:
Item[0] Attribute[0]
Item[0] Attribute[1]
Item[0] Attribute[2]
Item[1] Attribute[0]
Item[1] Attribute[1]
Item[1] Attribute[2]
Item[2] Attribute[0]
Item[2] Attribute[1]
Item[2] Attribute[2]
I have filed a Feedback for this crash, which occurs when running this project on both the macOS Sonoma beta and the iOS 17.0 iPhone 14 Pro simulator.
Should this work? Is there a workaround? Is there an alternate method to accomplish the same results?
**** BTW, this same sorting pattern has worked well with Core Data in the past.
import SwiftUI
import SwiftData
@Model
final class Item {
var name: String
var order: Int
@Relationship(.cascade) var attributes: [Attribute] = []
init(name: String, order: Int) {
self.name = name
self.order = order
}
}
@Model
final class Attribute {
var name: String
var order: Int
@Relationship var item: Item
init(item: Item, name: String, order: Int) {
self.item = item
self.name = name
self.order = order
}
}
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query(FetchDescriptor<Attribute>(sortBy: [SortDescriptor(\Attribute.order, order: .forward)]))
// @Query(FetchDescriptor<Attribute>(sortBy: [SortDescriptor(\Attribute.item.order, order: .forward),
// SortDescriptor(\Attribute.order, order: .forward)]))
private var attributes: [Attribute]
var body: some View {
NavigationView {
List {
ForEach(self.attributes) { attribute in
Text("Item[\(attribute.item.order)] Attribute[\(attribute.order)]")
.monospaced()
}
}
.toolbar {
ToolbarItem {
Button("Setup", action: self.setup)
}
}
}
}
private func setup() {
withAnimation {
do {
let attributeDescriptor = FetchDescriptor<Attribute>()
let attributes = try self.modelContext.fetch(attributeDescriptor)
for attribute in attributes {
self.modelContext.delete(attribute)
}
let itemDescriptor = FetchDescriptor<Item>()
let items = try self.modelContext.fetch(itemDescriptor)
for item in items {
self.modelContext.delete(item)
}
try self.modelContext.save()
let item1 = Item(name: "Z", order: 0)
self.modelContext.insert(Attribute(item: item1, name: "\(item1.name).0", order: 0))
self.modelContext.insert(Attribute(item: item1, name: "\(item1.name).1", order: 1))
self.modelContext.insert(Attribute(item: item1, name: "\(item1.name).2", order: 2))
self.modelContext.insert(item1)
let item2 = Item(name: "Y", order: 1)
self.modelContext.insert(Attribute(item: item2, name: "\(item2.name).0", order: 0))
self.modelContext.insert(Attribute(item: item2, name: "\(item2.name).1", order: 1))
self.modelContext.insert(Attribute(item: item2, name: "\(item2.name).2", order: 2))
self.modelContext.insert(item2)
let item3 = Item(name: "X", order: 2)
self.modelContext.insert(Attribute(item: item3, name: "\(item3.name).0", order: 0))
self.modelContext.insert(Attribute(item: item3, name: "\(item3.name).1", order: 1))
self.modelContext.insert(Attribute(item: item3, name: "\(item3.name).2", order: 2))
self.modelContext.insert(item2)
try self.modelContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}