@FetchRequest not refreshing

I am writing a SwiftUI App using CoreData + CloudKit, based partly on sample code from `CoreDataCloudKitDemo`.


There is a MasterListView which presents a List of NavigationLinks of the available data, eg. Recents, Tags, etc. embedded in a Double Column Style NavigationView.


There are 6 @FetchRequest vars that collect the content for the different Sections in the List.


They look similar to this:

@FetchRequest(fetchRequest: ThingViewModel.fetchRequest(matching: .recent)) var recentThings:FetchedResults<Thing>

Where the .fetchRequest(matching: .recent) is producing a FetchRequest with Predicate and SortDescriptors.


The problems start when I use this in MultiTasking mode on an iPad. The user adds new data to the App using a Share Extension.

If I have the App side by side with another App that can share to it, the share is successful, I can see the data go into CoreData and the MasterListView re-draws by the logs but the actual data is not reliably refreshed.


I have logging in the MasterListView to tell me when it re-draws and when it receives a Notification from the core data stack that there has been an update.


The core data stack is running a NSPersistentHistoryChangeRequest, filtering out the added Thing successfully, so I know it is there.

I have tried everything I can think of, including things like:


NSManagedObjectContext.mergeChanges(fromRemoteContextSave:into:)


viewContext.stalenessInterval = 0

viewContext.refreshAllObjects()

viewContext.stalenessInterval = -1


And everything else I have found on SO ;-)



What am I missing?

Replies

I have got it working now but for what feel like all of the wrong reasons.

I moved the notification handler up a level, so that the view that contains all of the @FetchRequest is force refreshed by being re-constructed.


The solution feels dirty, I still beleive that the @FetchRequest should be able to trigger a data reload itself when the data changes.


The top level ContentView is now having to be very heavy handed IMHO. by:


struct ContentView: View {
    private var didUpdate = NotificationCenter.default.publisher(for: .coreDataRemoteChange)
    @State private var renderID = UUID()
    var body: some View {
        NavigationView {
            MasterListView()
                .navigationBarTitle(Text("Things"))
                .navigationBarItems(
                    trailing: EditButton()
                )
            Text("No Content").noContentStyle()
            Print("ContentView \(renderID)")
        }
        .id(self.renderID)         
        .onReceive(self.didUpdate) { _ in
            print("ContentView received .coreDataRemoteChange")
            self.renderID = UUID()
        }
        .navigationViewStyle(DoubleColumnNavigationViewStyle())
        .accentColor(.accent)
    }
}


... attaching a UUID to the NavigationView.


Because the notification is reliable and the data was actually being updated, techniques like


NSManagedObjectContext.mergeChanges(fromRemoteContextSave: [NSInsertedObjectsKey: addedThings], into: [taskContext])

are not actually required.


I would be surprised if this was the way we are intended to use @FetchRequest

Have you tried this?

Code Block
viewContext.automaticallyMergesChangesFromParent = true

  • I’ve added this line of code, and my data now updates in my view, but in my understanding, this shouldn’t be needed. If you have a view with the @FetchRequest property wrapper properly setup, that should be enough to constantly be updating the view when changes to the Core Data model occur.

Add a Comment