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