Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement UITableViewDataSourcePrefetching with an NSFetchedResultsController data source

I have a UITableView that gets data from an NSFetchedResultsController. To this end, i basically copied Apples example implementation from https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/nsfetchedresultscontroller.html.

Now at WWDC16 Apple announced the UITableViewDataSourcePrefetching protocol, which provides callbacks that let you prefetch data so it's already loaded when it's needed to be displayed by the tableview. I'm looking for an example on how to integrate this with the NSFetchedResultsController, because i can't figure out if i'm doing this correctly.

  • Should i simply create a dictionary, as an in-memory cache, to hold prefetched data and be used in cellForRowAtIndexPath instead of querying fetchedResultsController.object(at: indexPath) directly?
  • As i understand it, CoreData already caches fetched data automatically, so maybe i just need to call fetchedResultsController.object(at: indexPath) in the prefetch callbacks, to ensure the data gets cached?
  • Or shouldn't i prefetch data at all when using a fetched results controller, because i would work around the magical integration provided by Apple?
  • Or something else entirely?

*edit*: I found a slide from the Core Data talk at WWDC16 that supposedly explains this, but i understand it at all.

enter image description here

  • An async fetch request? I thought they don't work with NSFetchedResultsController. I guess that's why it's performed on the managedObjectContext directly?
  • The async fetch request is created from the results of calling .performFetch() on the NSFetchedResultsController. Nothings showing up until i call that. But since all the results are there after calling it, i don't get why i would need to prefetch them again.
like image 716
cargath Avatar asked Jun 27 '17 22:06

cargath


2 Answers

It will improve performance if your model has faults. Whether your models have faults or not depends on iOS optimization, and how you create your schema and relationships.

So to be safe your could add the prefetch code. I have the code for Swift 4.

func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
    let fetchRequest: NSFetchRequest<MyModel> = MyModel.fetchRequest()
    fetchRequest.returnsObjectsAsFaults = false
    let items = indexPaths.map { fetchedResultsController.object(at: $0) }
    fetchRequest.predicate = NSPredicate(format: "SELF IN %@", items)
    let asyncFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest)
    do {
        try fetchedResultsController.managedObjectContext.execute(asyncFetchRequest)
    } catch { }
}

What it does is pretty self explanatory. It runs an async fetch request on the managed object context, therefore resolving faults, if any.

like image 98
samwize Avatar answered Nov 09 '22 00:11

samwize


In short, you are creating a custom predicate to "warm" the predicted specific objects in the MOC. The idea, I think, is that the table view is telling you more targeted scroll target info (based on velocity etc.) about where it thinks the table view will end up. This allows you to execute an async request to pre-fetch these objects in the MOC so that by the time you are actually asking for properties on it, they're ready to go.

This is why you also have a nil completion on the async request. You aren't directly using the results because you may not actually be displaying any of these index paths yet. It also means it's unnecessary to do additional tracking (like in a Dictionary, etc.) of results when you get pre-fetched objects.

The real question is, if you are using an FRC with batching, pre-fetched properties, etc., is this really buying you anything to side-step it? I'm not really sure. I haven't noticed a huge difference between a pretty highly tuned FRC+UITableView with vs without pre-fetching on a table of around 1000 results. It's also possible by merely touching those objects that the FRC will see that (because you are sharing the same MOC) and then will run through it's property/relationship pre-fetching, etc. I haven't seen much documentation on this mechanism, but if Apple is suggesting it, especially using an FRC, I have to think it helps.

like image 33
Procrastin8 Avatar answered Nov 09 '22 01:11

Procrastin8