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)")
}
}
}
}