Shout out to pbasdf for helping me solve this issue.
Fixed code:
lazy var fetchedResultsController = self.getFetchedResultsController()
var sectionKeyPath: String? = #keyPath(Object.sectionKey)
var searchPredicate: NSCompoundPredicate?
// MARK: - Return FRC:
private func getFetchedResultsController() -> NSFetchedResultsController<Object> { // var fetchedResultsController:
print("Lazy: getFetchedResultsController()")
let fetchRequest: NSFetchRequest<Object> = Object.fetchRequest()
fetchRequest.predicate = searchPredicate
let sortByKey = NSSortDescriptor(key: #keyPath(Object.sectionKey), ascending: true)
let sortByName = NSSortDescriptor(key: #keyPath(Object.name), ascending: true)
fetchRequest.sortDescriptors = [sortByKey, sortByName]
fetchRequest.fetchBatchSize = 20
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: coreDataStack.managedContext,
sectionNameKeyPath: sectionKeyPath ?? nil,
cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}
private func refreshFRC() {
fetchedResultsController = getFetchedResultsController() // Reset FRC
do { // Load Data:
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Fetching error: \(error), \(error.userInfo)")
}
}
This gives you the FRC with an optional predicate and sectionNameKeyPath. Which you can then set to your needs, and then set the changes with refreshFRC().
I'm working on adding search to a tableview using a NSFetchedResultsController. My goal:
I have working code. And I can make the table do both depending on my sectionKey, I just cant figure out how to do both in the same build.
Is this normal behavior and I'm trying to do something thats not possible by changing the FRC's sectionNameKeyPath and sortDescriptors? Or am I just missing something?
private func getFetchedResultsController() -> NSFetchedResultsController<Object> {
let fetchRequest: NSFetchRequest<Object> = Object.fetchRequest()
let sortByKey = NSSortDescriptor(key: #keyPath(Object.sectionKey), ascending: true)
let sortByName = NSSortDescriptor(key: #keyPath(Object.name), ascending: true)
switch sectionKeyPath {
case nil:
fetchRequest.sortDescriptors = nil
fetchRequest.fetchBatchSize = 20
default:
fetchRequest.sortDescriptors = [sortByKey, sortByName]
fetchRequest.fetchBatchSize = 20
}
fetchRequest.sortDescriptors = [sortByKey, sortByName]
fetchRequest.fetchBatchSize = 20
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: coreDataStack.managedContext,
sectionNameKeyPath: sectionKeyPath ?? nil,
cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}
I'm also curious if it's better to use a a single FRC for the entire viewController, or if it would be a better approach to make one for the entire list of objects, and a second only for when the search is active?
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
searchBar.barStyle = .default
switch searchBar.text?.count {
case nil:
searchPredicate = nil
sectionKeyPath = #keyPath(Object.sectionKey)
tableView.reloadData()
case 0:
searchPredicate = nil
sectionKeyPath = #keyPath(Object.sectionKey)
tableView.reloadData()
default:
sectionKeyPath = nil
guard let searchText = searchBar.text else { return }
setSearchPredicate(search: searchText)
}
fetchFRC()
tableView.reloadData()
} // End: updateSearchResults()
func fetchFRC() {
do {
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Fetching error: \(error), \(error.userInfo)")
}
}
As per comments:
getFetchedResultsController
before fetchFRC
in that updateSearchResults
code; andfetchedResultsController
var defined in your view controller.