On Core Data Object Fetching and Display Strategies

Setting the Stage

Suppose one of the NSManagedObject subclasses from your Core Data data model looked like this:

1
2
3
4
5
6
public class BlogIdea: NSManagedObject {

    @NSManaged public var ideaTitle: String?
    @NSManaged public var ideaDescription: String?

}
Suppose that you also have a class that will provide BlogIdeas to your view controller:

1
2
3
class BlogIdeaProvider {
    // fetches BlogIdea instances to use in your view controller
}
Requirement: Say that you need to list all of the ideaTitles you have inside of a table view (note that you don’t need to display the ideaDescription at this time).

Question: Which of the following strategies would you recommend for this requirement?

1) Fetch all the BlogIdea objects, return them, and then use those returned NSManagedObject instances to display the ideaTitle in the table view.

1class BlogIdeaProvider {
2    func fetchBlogIdeas() -> [BlogIdea] {
3        // fetch BlogIdea instances
4
5        // return the BlogIdea NSManagedObject subclass for the view controller to use
6    }
7}

2) Fetch all of the BlogIdea objects, but make a fetchBlogIdeaTitles function instead. Just return the ideaTitles, say, in an array of strings to display in the table view.

1class BlogIdeaProvider {
2    func fetchBlogIdeaTitles() -> [String] {
3        // fetch BlogIdea instances
4
5        // pull out ONLY the ideaTitle, 
6        // assemble an array for the titles, 
7        // return the array
8    }
9}

Swifting Out Loud

I’ll give my recommendation and then walk through my reasoning.

In nearly all situations, I would go with option number 1: fetch the NSManagedObject instances that I need to display, and work with those fully-featured objects, rather than go through the effort to return only the Strings that I need for display.

Why?

To that, I’d ask the question: “What are you planning to do next, once the ideaTitles are displayed in the table view?

Presumably, you’re displaying them so that a user can tap on a cell and…do something with them. And that “do something with them” part almost always means

  • Show more details (in which case, you need the ideaDescription property as well now)
  • Edit the object (in which case you get all your saving features only if it’s in NSManagedObject form)
  • Delete the object (again…needs to be a full NSManagedObject to easily delete)

Efficiency Trade-offs

One might be concerned about efficiency – “Why return a full object when you only need one of its properties?”

That’s true. You only need one property…for now. Again, I think it goes back to “what happens next?”

Fetching Efficiency

Will you display the ideaTitle and then have to go fetch again to get the rest of what you need from your persistent store? That’s almost guaranteed to be less efficient than holding the full NSManagedObject in memory from the start.

Memory Efficiency

If you’re trying to stay memory-efficient, perhaps another idea is to fetch only a subset of the BlogIdeas… You could limit the number of results that come back (after all, only so many can be shown in a table view at a time, anyway, right?).

Code Efficiency

When you say “Core Data” and “table view” in the same sentence, it should also trigger the word NSFetchedResultsController. This class is a huuuuge help because it was designed specifically for displaying data from NSManagedObjects in table or collection views. If you only return an array of Strings, you’ll have to write a bunch of boiler plate code yourself to keep your table view in sync with your persistent store, but you get all of that for free with NSFetchedResultsController.

Using NSFetchedResultsController would change the code a bit. Instead of returning an array of blog ideas ([BlogIdea]), you could hold a reference to a NSFetchedResultsController<BlogIdea> and configure it to fetch:

 1// MARK: - Concept
 2// If you give a `Provider` a persistent container, and a fetched results controller delegate to talk back to,
 3// a `Provider` Type can act as a liaison between the view controller and the pieces of Core Data that are needed
 4// to initialize a fetched results controller, perform fetches, and other Core Data related actions on behalf of your
 5// view controller (instead of you putting all this code in the view controller itself)...
 6
 7class BlogIdeaProvider {
 8    private var persistentContainer: NSPersistentContainer
 9    private weak var fetchedResultsControllerDelegate: NSFetchedResultsControllerDelegate?
10    
11    // MARK: - Initializers
12    init(with persistentContainer: NSPersistentContainer, 
13              fetchedResultsControllerDelegate: NSFetchedResultsControllerDelegate?) {
14
15        self.persistentContainer = persistentContainer
16        self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate
17
18    }
19    
20    // use this in your view controller to display BlogIdea instances
21    lazy var fetchedResultsController : NSFetchedResultsController<BlogIdea> = {
22        // Configure a fetched results controller and perform an initial fetch
23        let blogIdeasFetchRequest = NSFetchRequest<BlogIdea>(entityName: "BlogIdea")
24        
25        let controller = NSFetchedResultsController<BlogIdea>(  fetchRequest: blogIdeasFetchRequest,
26                                                                managedObjectContext: self.persistentContainer.viewContext,
27                                                                sectionNameKeyPath: nil,
28                                                                cacheName: nil)
29        
30        controller.delegate = self.fetchedResultsControllerDelegate
31        
32        do {
33            try controller.performFetch()
34        } catch {
35            fatalError("\(#function): Failed to performFetch: \(error)")
36        }
37        
38        return controller
39    }()
40}

Concluding Thoughts

I know I keep going back to it, but I think it’s what will help make the decision.

What’s the next thing you’re expecting to happen after you fetch the objects and display them?

Let that be your guiding principle for your fetching and displaying strategy.

With Core Data, it’s almost always going to be more convenient to be working with NSManagedObjects. Start there, and handle efficiency problems as they arise. 🙌🏻

comments powered by Disqus