As the title says I'm trying to use a to-one relationship as a sectionIdentifier in a @SectionedFetchRequest. The compiler is happy but there's a runtime crash:
Could not cast value of type '_NSCoreDataTaggedObjectID' (0x146c0f750) to 'MyApp.ServiceCategory' (0x104c4b3a0).
The fetch request:
@SectionedFetchRequest(
sectionIdentifier: \Service.serviceCategory,
sortDescriptors: [
SortDescriptor(\Service.active, order: .reverse),
SortDescriptor(\Service.displayText)
],
predicate: NSPredicate(format: "%K = %d", #keyPath(Service.active), true),
animation: .default
) var sectionedServices: SectionedFetchResults<ServiceCategory?, Service>
... and the breaking runtime code:
ForEach(sectionedServices /* here */) { section in
Section(header: Text(section.id?.displayText ?? "")) {
ForEach(section) { svc in
Text(svc.displayText ?? "")
}
}
}
The request works if I switch out the sectionIdentifier for the active
property (which is a Bool property rather than a relationship). It also works if I switch it out for displayText
which is an optional String, so it seems to be a problem trying to section by a relationship rather than with it being an optional.
The error suggests the request is returning a Core Data fault rather than an object but my attempts to somehow unwrap this haven't gone very far.
Any thoughts would be greatly appreciated!
As hinted in the error message, you're attempting to cast the type ServiceCategory
in a manner that SwiftUI cannot manage.
The var sectionIdentifier
is expecting a reference to a ServiceCategory
as shown in this line
) var sectionedServices: SectionedFetchResults<ServiceCategory?, Service>
but instead you are passing the faulted object ID _NSCoreDataTaggedObjectID
per your line
sectionIdentifier: \Service.serviceCategory,
Service.serviceCategory holds a reference to an entity instance... in your situation the ServiceCategory
entity. If you place a break point in your code and query the value you'll probably see a reference to a record id for that entity, not the actual entity object.
Frankly I'd recommend that you change your approach a little write an extension on your Service
entity and include a convenience method to grab this value...
extension Service: NSManagedObject {
@objc var serviceCategoryName: String {
return self.serviceCategory.name ?? "NO NAME"
}
}
then use this as follows...
@SectionedFetchRequest(
sectionIdentifier: \.serviceCategoryName,
sortDescriptors: [
SortDescriptor(\.active, order: .reverse),
SortDescriptor(\.displayText)
],
predicate: NSPredicate(format: "%K = %d", #keyPath(.active), true),
animation: .default
) var sectionedServices: SectionedFetchResults<String, Service>
Note the change from type ServiceCategory
to type String
.
and in the list...
ForEach(sectionedServices) { section in
Section(header: Text(section.id)) {
ForEach(section) { service in
Text(service.displayText ?? "")
}
}
}