DEV Community

Filip Němeček
Filip Němeček

Posted on • Updated on • Originally published at nemecek.be

How to use Core Data with Diffable Data Source - a minimal example

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.

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.

I write about iOS/Swift here and on my blog nemecek.be

Top comments (0)