SortDescriptor from a generic method crashes Xcode Previews but not Simulator.

I attempted to create a SortDescriptor using a method in enum to sort various different models in SwiftUI lists. But it causes crashes in Xcodes previews but not in the simulator, and I don't know why. Maybe someone could suggest changes.

extension Item:ListSorting {} //List sorting is a protocol given to various Models
enum ListSortingStyle<T>:String,CaseIterable where T:ListSorting {
case alphabetical = "A…Z"
case alphabeticalReverse = "Z…A"
case createDate = "Newest"
case createDateReverse = "Oldest"
case editDate = "Latest Edit"
case editDateReverse = "Oldest Edit"
/// Returns a SortDescriptor matching the enumeration
func sortDescriptor() -> SortDescriptor<T> {
switch self {
case .alphabetical:
SortDescriptor<T>(\.name, comparator: .localized, order: .forward)
case .alphabeticalReverse:
SortDescriptor<T>(\.name, comparator: .localized, order: .reverse)
case .createDate:
SortDescriptor<T>(\.creationDate, order: .forward)
case .createDateReverse:
SortDescriptor<T>(\.creationDate, order: .reverse)
case .editDate:
SortDescriptor<T>(\.editDate, order: .forward)
case .editDateReverse:
SortDescriptor<T>(\.editDate, order: .reverse)
}
}
}

Which is used in

struct ItemsList: View {
@Environment(\.modelContext) private var modelContext
@Query private var items:[Item] = []
init(sortStyle:ListSortingStyle<Item> = .alphabetical) {
let sortDescriptor = SortDescriptor<Item>(\.name, comparator: .localized, order: .forward) //(1) This works in preview & simulator
//OR
let sortDescriptor2 = sortStyle.sortDescriptor() //(2) This crashes in a preview when used in a fetch not in the iOS simulator
print(sortDescriptor,sortDescriptor2)
let descriptor = FetchDescriptor<Item>(sortBy:[sortDescriptor2])
self._items = Query(descriptor)
}
var body: some View {
...
}
}

As far as I can see both methods create a SortDiscriptor. If I print the sortDescriptor variable in they look almost identical:

//1
SortDescriptor<Item>(
order: Foundation.SortOrder.forward,
keyString: nil,
comparison: Foundation.SortDescriptor<MyApp.Item>.AllowedComparison.comparableString(
(extension in Foundation):Swift.String.StandardComparator(options: __C.NSStringCompareOptions(rawValue: 0),
isLocalized: true,
order: Foundation.SortOrder.forward),
\Item. <computed 0x0000000340213b18 (String)>)
)
//2
SortDescriptor<Item>(
order: Foundation.SortOrder.forward,
keyString: nil,
comparison: Foundation.SortDescriptor<MyApp.Item>.AllowedComparison.comparableString(
(extension in Foundation):Swift.String.StandardComparator(options: __C.NSStringCompareOptions(rawValue: 0),
isLocalized: true,
order: Foundation.SortOrder.forward),
\Item.<computed 0x0000000340022174 (String)>)
)

But the one created with the generic method on the enum crashes with the error message:

CrashReportError: Fatal Error in DataUtilities.swift
MyApp crashed due to fatalError in DataUtilities.swift at line 64.
Couldn't find \Item.<computed 0x0000000340022174 (String)> on Item with fields [SwiftData.Schema.PropertyMetadata ...

Hi,

Sorry to hear you are having problems getting previews working. This most commonly is seen when previews don't have a SwiftData model container configured correctly.

Could you show how you are setting up your preview? I am looking for your #Preview definition + any custom PreviewModifier s you are using.

Thank you for your time replying. Yeah I had many problems a year or two ago in getting the container right. But I think it ok at the moment especially as it works fine creating a SortDescriptor one way, but creating it the other way (without changing anything else) causes the crash. I copied only the necessary code to a basic app today so the whole thing fits in here. There are actually 2 files with previews and they add the model container in different ways: .modelContainer(PreviewDataController.previewContainer) .modelContainer(for: Item.self, inMemory: true)

So the problem is probably else where.

import SwiftData
@main
struct MyAppApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Item.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
}
import SwiftData
struct ContentView: View {
@Query private var items: [Item]
var body: some View {
NavigationView{
ItemsList(sortStyle: .alphabetical)
}
}
}
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}
import SwiftUI
import SwiftData
struct ItemsList: View {
@Environment(\.modelContext) private var modelContext
@Query private var items:[Item] = []
init(sortStyle:ListSortingStyle<Item> = .alphabetical) {
let sortDescriptor1 = SortDescriptor<Item>(\.name, comparator: .localized, order: .forward) //this works in both
let sortDescriptor2 = sortStyle.sortDescriptor() //this works in simulator but not in preview
print(sortDescriptor1,"\n",sortDescriptor2)
let fetchDescriptor = FetchDescriptor<Item>(sortBy:[sortDescriptor1]) // <-- change between 1&2 to reproduce the crash
self._items = Query(fetchDescriptor)
}
var body: some View {
List {
ForEach(items) { item in
NavigationLink {
Text(item.name)
} label: {
Text("\(item.name)")
}
}
}
.toolbar {
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
}
private func addItem() {
withAnimation {
let newItem = Item(name: "New Item")
modelContext.insert(newItem)
}
}
}
#Preview {
NavigationView{
ItemsList(sortStyle: .alphabetical)
.modelContainer(PreviewDataController.previewContainer)
}
}
import Foundation
import SwiftData
@MainActor
class PreviewDataController {
static let previewContainer: ModelContainer = {
do {
let schema = Schema([
Item.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
let container = try ModelContainer(for: schema, configurations: [modelConfiguration])
return container
} catch {
fatalError("Failed to create container for preview: \(error.localizedDescription)")
}
}()
/// A Protocol for models to help get predicates etc to sort
protocol ListSorting {
var name:String {get}
}
extension Item:ListSorting {}
enum ListSortingStyle<T>:String,CaseIterable where T:ListSorting {
case alphabetical = "A…Z"
case alphabeticalReverse = "Z…A"
/// Returns a SortDescriptor matching the case
func sortDescriptor() -> SortDescriptor<T> {
switch self {
case .alphabetical:
SortDescriptor<T>(\.name, comparator: .localized, order: .forward)
case .alphabeticalReverse:
SortDescriptor<T>(\.name, comparator: .localized, order: .reverse)
}
}
}
import Foundation
import SwiftData
@Model
final class Item {
var name:String = ""
init(name: String) {
self.name = name
}
}

It looks like a similar issue I am facing: https://developer.apple.com/forums/thread/775624

SortDescriptor from a generic method crashes Xcode Previews but not Simulator.
 
 
Q