SectionedFetchRequest not updating order of Sections

I have a SectionedFetchRequest that almost works as expected.

My data model has two Entities (Item and Attribute). Both entities have a name attribute and an order attribute. The Attribute entity has a to-One relationship to an Item called item. The Attribute entity also has a computed property:

    @objc var sectionName: String {
        get { return self.item.name }

The SectionedFetchRequest's sectionIdentifier key is \Attribute.sectionName and the sortDescriptors keys are \Attribute.item.order and then \Attribute.order. This sorts groups of Attribute's into Sections ordered by the Attribute item's order property. Each section is then ordered by the Attribute's own order property.

As expected, when the order property of any Attribute object in the results changes, that section of the View is updated to reflect the change. However, if any of the Attribute's related Item order properties change, the View is not updated.

It almost seems like SectionedFetchRequest is handling the NSManagedObjectContextDidSave notifications for changes to Attribute objects, but ignoring changes to Item objects that that should cause the results order to be changed and therefore the View to be updated.

Is this a bug in SectionedFetchRequest or is there a direct way that I can get the SectionedFetchRequest to change its state so SwiftUI can update the View as expected?

When I make a change to an Item's order, I can force the results to be updated by dynamically changing the nsPredicate with something that's different, but something that does not actually change the results. However, it seems like there should be a more appropriate way to force the SectionedFetchRequest to update the order of the Sections even if no changes are made to the actual Attribute objects that fetched.

I have created a sample project on GitHub that demonstrates this issue. SectionedFetchRequest_Experiment Explanations and expectations are in the comments of the ContentView.

While waiting formal solution, as a workaround, the general idea is to receive NSManagedObjectContextDidSave notifications and then check to see if any of the updated objects have any relationship to any of the objects in the SectionedFetchResults. If so, the nsPredicate dynamic property is changed to cause the results to be refetched, which will in turn cause the ContentView to be updated.

The GitHub sample project above has been edited to incorporate this workaround. Just uncomment the .onReceive view modifier to enable the workaround.

I still believe this work should be handled by the @SectionedFetchRequest property wrapper itself. However, I'm sure that a completely general solution would require more than what I have implemented here. I will submit a request via Feedback Assistant for this issue.

Just came across this looking for info on @SectionedFetchRequest. Better late than never 😛.

See the accepted answer here: SwiftUI List not updating when core data property is updated in other view. This is a kludge of sorts, but a clever one that only adds two lines of code to fix the problem - no need for your workaround. SwiftUI updates the view when the view state changes, and you are changing the core data in view but with nothing to notify the view of the changes. The refreshID does that, causing the view to refresh when a swap is executed.

I consider this Apple's bug. The whole point of using @FetchRequest and @SectionedFetchRequest is make Core Data objects 'published' so they can be observed in view - but as you show quite clearly it doesn't work properly. IMHO any change to the underlying data in Core Data should force the view to update automatically.

One thing that occurred to me as I wrote this - maybe putting all the swap/reset, fetches etc in a VM than publishing from there would work (not sure, and I don't have time to try it), but that defeats the purpose of @FetchRequest and @SectionedFetchRequest.

As an aside and in case I don't have time to provide an answer to your question, I humbly suggest that you should consider changing the entity name / your NSManagedObject subclass for Attribute to something that does not describe a part of a Core Data object graph.

I cannot make @SectionedFetchRequest work with a SortDescriptor where the value is obtained from traversing a Core Data entity relationship.

Your example: item.name

In fact in Xcode Version 13.1 the compiler presents me with the particularly informative "The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions" and subsequently my project/s will not build.

SO this is how I get around the issue (using your example)...

I add a new attribute named sectionIdentifier of type String or Integer 64 optional (by default) and if Integer 64 then not scalar to the Core Data entity, in your case the entity named Attribute. With Codegen set to Class Definition, Core Data framework automatically creates an NSManagedObject with property type String? or NSNumber? respectively.

Wherever the data for an instance of Attribute is entered by the user (or you), add the following .onChange modifier.

(where attribute is defined by @ObservedObject var attribute: Attribute)

.onChange(of: attribute.item) { changed in                 // or because we're not using changed, then { _ in
    if let item = attribute.item {
        let itemOrder = String(format: "%02d", item.order) // if you set sectionIdentifier type as String, OR
        let itemOrder = item.order                         // if you set sectionIdentifier type as Integer 64
        attribute.sectionIdentifier = itemOrder
    }
    else {
        attribute.sectionIdentifier = "0"                  // if you set sectionIdentifier type as String, OR
        attribute.sectionIdentifier = 0                    // if you set sectionIdentifier type as Integer 64
    }
}

Change a @SectionedFetchRequest to the following (specifically note the first SortDescriptor).

@SectionedFetchRequest(sectionIdentifier: \.sectionName,
                       sortDescriptors: [
                         SortDescriptor(\.sectionIdentifier, order: .reverse),
                         SortDescriptor(\.order, order: .reverse)],
                       animation: .default
) var attributes: SectionedFetchResults<String, Attribute>

This should produce a dynamically changing SectionedFetchResults for var attributes and subsequently update any List or similar view whenever order changes for an instance of Item.

SectionedFetchRequest not updating order of Sections
 
 
Q