In SwiftUI in iOS 18.1, `SectionedFetchRequest` is not refreshed when changes are done to the fetched entity's attributes.

Hi,

This issue started with iOS 18, in iOS 17 it worked correctly. I think there was a change in SectionedFetchRequest so maybe I missed it but it did work in iOS 17.

I have a List that uses SectionedFetchRequest to show entries from CoreData. The setup is like this:

struct ManageBooksView: View {
    @SectionedFetchRequest<Int16, MyBooks>(
        sectionIdentifier: \.groupType,
        sortDescriptors: [SortDescriptor(\.groupType), SortDescriptor(\.name)]
    )
    private var books: SectionedFetchResults<Int16, MyBooks>

    var body: some View {
        NavigationStack {
            List {
                ForEach(books) { section in
                    Section(header: Text(section.id)) {
                        ForEach(section) { book in
                            NavigationLink {
                                EditView(book: book)
                            } label: {
                                Text(book.name)
                            }
                        }
                    }
                }
            }
            .listStyle(.insetGrouped)
        }
    }
}
struct EditView: View {
       private var book: MyBooks
    init(book: MyBooks) {
       print("Init hit")
       self.book = book
    }
}

Test 1: So now when I change name of the Book entity inside the EditView and do save on the view context and go back, the custom EditView is correctly hit again.

Test 2: If I do the same changes on a different attribute of the Book entity the custom init of EditView is not hit and it is stuck with the initial result from SectionedFetchResults.

I also noticed that if I remove SortDescriptor(\.name) from the sortDescriptors and do Test 1, it not longer works even for name, so it looks like the only "observed" change is on the attributes inside sortDescriptors.

Any suggestions will be helpful, thank you.

While waiting for some official statement why this is happening in iOS 18 and not iOS 17, I found a workaround to force a refresh of the managedObjectContext when changes are saved.

Code sample:

@Environment(\.managedObjectContext) private var managedObjectContext

var body: some View {
    NavigationStack {
        Text("")
    }
    .onReceive(NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)) { _ in
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            withAnimation {
                managedObjectContext.refreshAllObjects()
            }
        }
    }
}

It's not perfect but it gets the job done to update the list when any attributes are changed, not only those used as SortDescriptor. Hope this helps anyone facing this problem.

What you described probably happens because SwiftUI believes the result set of SectionedFetchRequest doesn't change, which is because:

  • The managed objects aren't changed. When checking the equality of Core Data managed objects, the system may use objectID, and in your case, changing an attribute doesn't change the object ID.

  • The order of the list item doesn't change.

NSManagedObject is not observable, but you can use ObservedObject to publish the changes:

struct EditView: View {
    //private var book: MyBooks
    @ObservedObject var book: MyBooks
    ...
}

With that, the change that EditView makes on an object should be published and be detected by SwiftUI.

If that doesn't work, please provide a workable code snippet that reproduces the issue. I'd take a further look.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Hi @DTS Engineer Thank for responding to my topic.

Marking the object with ObservedObject does not resolve the issue, the change is still not propagated back to the list that uses SectionedFetchRequest. I created a runnable project from the Xcode templates that shows the issue.

  • Clone this repo https://github.com/VladimirAmiorkov/SwiftUI-SectionedFetchRequest
  • Open the Edit List Test.xcodeproj
  • Run the project
  • Press the "+" to add some items, notice the "Text- Init 0"
  • Tap on the row to pen EditView
  • Notice the "Text- Init 0" that is show inside EditView
  • Tap on the button "Change text"
  • Notice the "Text- Init 0 ..." that is show inside EditView changed
  • Go back
  • Notice the "Text- Init 0" in the List is not updated (issue happens here)
  • Restart app
  • Notice the "Text- Init 0" in the List is updated

Same code works correct and updates the list if I switch from SectionedFetchRequest to FetchRequest. You can uncomment it in the project.

Or it also works if I add SortDescriptor(\.text) to the SectionedFetchRequest but that is not what I want as I do not want to list all "edible" entities of the object in the request.

Thank you for looking into this.

In SwiftUI in iOS 18.1, `SectionedFetchRequest` is not refreshed when changes are done to the fetched entity's attributes.
 
 
Q