Slow List refresh when using coredata NSFetchedResultsController

I'm making a test with SwiftUI and my app (that uses coredata) and I'm having some problems.


I managed to make to work NSFetchedResultsController and it works nicely. The problem occurs when I made searches (changing the predicate) and the List must update the changes. It seems that not only the visible "rows" are compared between the old and new fetchedObjets (as before with the old UITableView), but the entire list and that is extremely slow.


The logs that I'm seeing with SQLDebug level 4 are showing me that all objets are being loaded in batches of 20 objets (fetchBatchSize = 20).


This is my code for the List View:


struct OrderListView: View {
    
    @ObservedObject var searchOrderFilter: SearchOrderFilter
    
    var body: some View {

        List {
            SearchView(searchText: $searchOrderFilter.searchText, selectedOption: $searchOrderFilter.selectedOptionIndex, placeholder: "Search", options: ["Address", "Work", "Client", "Phone", "PO"])

            ForEach(searchOrderFilter.fetchedResultsController.fetchedObjects!, id: \.objectID) { (order) in
                OrderCellView(order: order)
            }
            
        }
        .navigationBarItems(trailing: HStack(alignment: .center, spacing: 20.0) {
            Button(action: {}, label: { Text("Select") })
            Button(action: {}, label: { Text("Filter") })
            Button(action: {}, label: { Text("+") })
        })
        .navigationBarBackButtonHidden(false)
        .listStyle(DefaultListStyle())
    }
}

And this is my current (testing) SearchOrderFilter class:


final class SearchOrderFilter: NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
    
    public let objectWillChange = PassthroughSubject<SearchOrderFilter, Never>()
    
    private var filterType: Order.FilterType
    
    @Published var searchText: String = "" {
        didSet {
            if oldValue != searchText {
                fetchOrders()
            }
        }
    }
    @Published var selectedOptionIndex: Int = 0 {
        didSet {
            if oldValue != selectedOptionIndex {
                fetchOrders()
            }
        }
    }
        
    public func setFilter(filterType: Order.FilterType) -> SearchOrderFilter {
        
        if self.filterType != filterType {
            self.filterType = filterType
            self.searchText = ""
            self.selectedOptionIndex = 0
            fetchOrders()
        }
        return self
    }
    
    init(filterType: Order.FilterType) {
        
        self.filterType = filterType
        super.init()
        
        updateCounterForCommonFilter()
    }
        
    
    public var fetchedResultsController: NSFetchedResultsController<Order> = NSFetchedResultsController(fetchRequest: Order.filteredFetchRequest(filterType: .none), managedObjectContext: CoreDataCache.sharedInstance.persistentContainer.viewContext, sectionNameKeyPathme:
    
    
    private func fetchOrders() {
             
        let filterSecondaryType = Order.FilterSecondaryType(rawValue: selectedOptionIndex) ?? Order.FilterSecondaryType.none
        let fetchRequest = Order.filteredFetchRequest(filterType: self.filterType, filterBy: filterSecondaryType, se
        fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: CoreDataCache.sharedInstance.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
        fetchedResultsController.delegate = self
        //_fetchedResultsController.fetchRequest.predicate = fetchRequest.predicate
                
        do {
            print("perform fetch")
            try fetchedResultsController.performFetch()
            
        } catch {
            objectWillChange.send(self)
            //_fetchedResultsController = nil
        }
    }
    
    // MARK: NSFetchedResultsControllerDelegate
    
    func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        print("controller will change content")
        objectWillChange.send(self)
    }
}

Forgive me if the last code doesn't look right, I had some problems copying it and it's my test class (where I'm trying many different approches).


I also tried using the @FetchRequest but I couldn't find how I can change the predicate when I'm doing new searches.


If someone has a tip or any comment, I'd appreciate it.


Thank you

Replies

Hello,


Did you get resolution on this? Im thinking its a problem with SwiftUI. I have posted about a similar issue:


https://stackoverflow.com/questions/58383419/swiftui-coredata-fetch-is-very-slow


and I have also put in a code leve assistance request with Apple.


Any advice on whay you've done to correct this would be greatly apprecioated..


Thank You

Any progress? I am seeing this same issue.

I am seeing this same issue.