Minimal example of using Diffable Data Source with Core Data

The new Diffable Data Source is totally different way to setup Table Views and Collection Views. This example will get you started.

Published: Aug. 30, 2020
App Store

If you have been developing iOS apps for some time, setting up Table Views and Collection Views is likely muscle memory. You prepare the UI component, set datasource and delegate and then implement methods that tell said component how many sections and rows in each section to accept. The last needed step is to configure cell for each row.

Using this old setup with Core Data means in most cases NSFetchedResultsController and implementation of its delegate which tells you when data content changed and you need to update the Table/Collection View accordingly.

Diffable Data Sources make this much easier. But also the process is totally different and can be a bit hard to get used to.

I am writing this short example for myself and other to speed up setting up Diffable in new projects with Core Data. The result is three shorter methods that make the initial collection view work.

I am using UICollectionView in this example, but UITableView is basically identical.

Note: In your project substitute Model class from my example with your own Core Data entity.

Preparing properties

Let's start with declaring properties:

var datasource: UICollectionViewDiffableDataSource<Int, Model>!
var fetchedResultsController: NSFetchedResultsController<Model>!

Note: For UITableView you would use UITableViewDiffableDataSource. The Int is used because this requires type for section. If you have just one you can use Int, String and then pass either "0" or empty string.

Configuring UICollectionViewDiffableDataSource

Next we configure the datasource like so:

func configureDatasource() {
    datasource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { (collectionView, indexPath, scan) -> UICollectionViewCell? in
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ModelCell", for: indexPath) as! ModelCell
        cell.configure(with: model)
        return cell
    })
}

All we need to do is to create an instance of UICollectionViewDiffableDataSource. We give it our collectionView and configure cellProvider which is modern version of the old cellForRowAt method.

This could be separate method we could pass in. Since this is an example and I have just a single I am using inline version.

NSFetchedResultsController

Time to configure the NSFetchedResultsController:

func initFetchedResultsController() {
    fetchedResultsController = NSFetchedResultsController(fetchRequest: Model.sortedFetchRequest, managedObjectContext: Database.shared.context, sectionNameKeyPath: nil, cacheName: nil)
    fetchedResultsController.delegate = self
    try! fetchedResultsController.performFetch()
}

Animating changes

And almost as a last step we need to implement a method that will update our collectionView when data changes:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
    var diffableDataSourceSnapshot = NSDiffableDataSourceSnapshot<Int, Model>()
    diffableDataSourceSnapshot.appendSections([0])
    diffableDataSourceSnapshot.appendItems(fetchedResultsController.fetchedObjects ?? [])
    datasource?.apply(diffableDataSourceSnapshot, animatingDifferences: view.window != nil)
}

And lastly we need to call these methods in viewDidLoad:

override func viewDidLoad() {
    super.viewDidLoad()
    configureDatasource()
    initFetchedResultsController()
}

Of course this solution is not perfect and is not meant to be. My goal was to show simplest working example to get started which you can tweak to your liking.

Uses: Xcode 12 & Swift 5.3

Bluesky logo

Follow on Bluesky to not miss new posts

Filip Němeček profile photo

WRITTEN BY

Filip Němeček Mastodon

iOS blogger and developer with interest in Python/Django.

iOS blogger and developer with interest in Python/Django.