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)