Skie Skie - 20 days ago 11
Swift Question

Working with big base in Core Data via fetchedResultsController

Maybe someone has experience of work with big Core Data bases (i have 32k of rows in my base, and i need to show all this base to the user and make some search in it) and when i'll try to read the base in to my fetchedResultsController i have 3-5 seconds delay, how i can fix this?
Can i read this base in some background thread?
or if i make separate my base on some parts via relations is it help?
i try to load my base in viewDidAppear

override func viewDidAppear(_ animated: Bool) {
DispatchQueue.main.async {
self.connectFetchedRequest()
self.tableView.reloadData()
self.indicatorLoad.stopAnimating()
}
}

func connectFetchedRequest() {
do {
try self.fetchedResultsController.performFetch()
}catch {
print(error)
}
}


for search in the base i use UISearchBarDelegate method (but i still have some "lags" when add characters to the searchText)

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
block?.cancel()
block = DispatchWorkItem {
let predicate = NSPredicate(format: "cardName contains[c] %@", searchText)
self.fetchedResultsController.fetchRequest.predicate = predicate
self.connectFetchedRequest()
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 0.5, execute: block!)
}


i created fetchedResultsController with the following code

lazy var context: NSManagedObjectContext = {
let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
return appDelegate!.managedObjectContext
}()

lazy var fetchedResultsController: NSFetchedResultsController<CardsBaseClass> = {
let fetchRequest = NSFetchRequest<CardsBaseClass>(entityName: "CardsBase")
let sortDescriptor = NSSortDescriptor(key: "cardName", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]

fetchRequest.fetchBatchSize = 50
fetchRequest.returnsObjectsAsFaults = false

let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.context, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}()

Answer

Here's some guidelines:

  1. Use batching. Set fetchBatchSize of your fetch request to something like 50, and returnsObjectsAsFaults to NO.
  2. Use estimated heights in your table view. Either estimatedRowHeight or -tableView:estimatedHeightForRowAtIndexPath: of UITableViewDelegate will do.

This will prevent loading all of the 32K records in memory on each reload.