0

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
    }()

for add new predicates from other viewController (which i open .overFullScreen so my main view with base is open too) i use the following code

predicateArrayClass.addPredicate(predicate: predicateType)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateTableViewByNewPredicate"), object: nil, userInfo: nil)
self.dismiss(animated: true, completion: nil)

where i called the update fetchResultController func

func updateTableViewByNewPredicate() {
        searchController.searchBar.text! = ""
        let predicateArray = predicateArrayClass.arrayOfPredicates
        block = DispatchWorkItem {
            self.fetchedResultsController.fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicateArray)
            self.connectFetchedRequest()
            DispatchQueue.main.async(execute: {
                self.tableView.reloadData()
            })
        }
        DispatchQueue.global(qos: .background).async(execute: block!)
}
Skie
  • 91
  • 1
  • 2
  • 13
  • Can you edit your question to add the code where you create the fetched results controller? How you configure it is probably a critical detail. – Tom Harrington Nov 15 '16 at 18:18
  • added, and also i use NSFetcherResultControllerDelegate methods for update tableView data (controllerWillChangeContent, controllerDidChangeContent and didChangeObject). I added the code in the answer by "teapot" but loading time is still several seconds – Skie Nov 15 '16 at 20:09

2 Answers2

1

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.

bteapot
  • 1,897
  • 16
  • 24
  • thanks, work with base is better now but still have some loading time at start when base is connecting, is it to real to fix this or it is the time which need to connect the base in fetchedResultsController? – Skie Nov 15 '16 at 20:24
  • 1
    @Skie You can warm up a NSPersistentStoreCoordinator's cache by executing background fetch request and passing NSManagedObjectIDs to main thread, maybe right after application start. – bteapot Nov 15 '16 at 22:06
1

It's not because you have 32k records. That's not big. The problem is that you are apparently attempting to sort those records using a string attribute:

let sortDescriptor = NSSortDescriptor(key: "cardName", ascending: true)

Sorting 32k records by a string value is a lot of work. It's going to take time. There are a couple of possibilities for speeding it up:

  1. If possible, sort by a numeric or date attribute instead of by a string. This is much faster. If your data doesn't have a useful numeric attribute, maybe you can make one up that will give you the same sorting results.
  2. Tell Core Data to index this attribute by checking the "indexed" box in the model editor. This will help, but not as much as sorting on a numeric attribute.
Tom Harrington
  • 69,312
  • 10
  • 146
  • 170
  • thanks, i added "indexed" for my "cardName" and all base was sorted very quickly now. I have a new question. I have the update some predicates for filtering in this base from other viewController, and the first filter loading about 10 seconds, but when i use this and/or new predicates filters secondary i have only 1 sec delay for reloding the search result. Why this happends? Maybe i called the new base in the memory (i trying to catch this but i don't call any new NSFetchRequestControllers) Or its not good method for updating the fetchedResultsController? – Skie Nov 17 '16 at 00:14