First steps with UITableViewDiffableDataSource — Part 2

Roberto Sampaio
My iOS Studies
Published in
4 min readApr 16, 2021

In the first part of this study, I explained some problems that we face when we use UITableViewDataSource, and I presented an alternative, introduced by Apple on WWDC 2019: UITableViewDiffableDataSource.

In the case you didn't read this study, you can check it here, if you want. You can check here the base project for this study and for the next ones.

Let's continue!

Photo by Jessie Kenaston on Unsplash

Where were we?

We just initialized the UITableViewDiffableDataSource and applied the first snapshot with it's initial elements. The problem is that we can't edit, move or add new elements.

Let's fix that step by step.

The first thing to understand is that UITableViewDiffableDataSource has the responsibility to tell if the cell is editable or movable. It was a role of the UITableViewDelegate, but now it belongs to UITableViewDiffableDataSource.

We can remove this piece of code from protocol implementation of UITableViewDelegate.

extension DiaryViewController: UITableViewDelegate {
//Delete all the other methods. Don't delete only the one below.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
//Don't delete it's implementation!

Now time to make our UITableViewDiffableDataSource able to move or edit notes. To do that, we need to create a new class, subclass of UITableViewDiffableDataSource. This new class should override the following methods:

@objc open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool@objc open func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool

We don't have any situation that any of these method should return false. So, both should only return true.

Before we continue with dataSource implementation we should fix something important.

Our dataSource is a type that keep notes. Even it's showing correctly, we will have some problems.

When we update an element and apply a snapshot, the tableView will be updated based on the changes between the old and the current snapshot. In our case, the hash value is based only by the id. So, there's no change between the elements, because we don't update the id when we edit a note. We just update the title and the message.

If we try to force an update, combining all the elements to have a different hash, something like…

func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(title)
hasher.combine(message)
}
static func == (lhs: DiaryNote, rhs: DiaryNote) -> Bool {
return lhs.id == rhs.id &&
lhs.title == rhs.title &&
lhs.message == rhs.id
}

… we will have another problem! :(

In this case, when we change the title or message, we have a new note different from the old one. The snapshot will add a new element, instead of updating the existing note.

Well… What to do?

Photo by Jonathan Cosens Photography on Unsplash

An interesting approach would be, instead of using notes as one of the dataSource types, we could use it's id's.

Personal note: The approach of keeping an array of id's, and have the responsibility of sync it with the tableView first looked to me, like a return to the past problems. Also, it can lead to crash the app if we don't sync it correctly, like before. But, despite of that, I think it's safer, because we use the id instead of indexPath to build the cells and updates the tableView.

After that, we need to update everything on our code to conform to the new change.

Take your time to take a look in the code below, before you continue reading. :)

As you probably noticed, in the saving function, we are adding and updating the notes array, every time we add or update a note. Then we apply the snapshot. We want to have everything in sync!

Maybe you're asking to yourself why DiaryNote is still Hashable. We're only using the id now. Yeah, you're right! We don't need it as Hashable anymore.

But, as we're using firstIndex(of: on save function, it needs to conform at least toEquatable.

The DiaryNote code could be like that:

struct DiaryNote: Equatable {
let id = UUID()
var title: String
var message: String
static func == (lhs: DiaryNote, rhs: DiaryNote) -> Bool {
return lhs.id == rhs.id
}
}

The dataSource changed as well. It's an EditEnabledDiffableDataSource now. It already changed to use the UUID and it has as a closure for the callback after deleting an element.

This deleteClosure is used to delete the element in the controller, because we can't access the notes array in the new dataSource.

Cool, isn’t it?

Let's take a look at the code!

Now we can run the project!

Voilá! Everything is working like before!

Photo by Joe Caione on Unsplash

Done! This is the end of our study about UITableViewDIffableDataSource.

The final code you can find at the study01-final branch in the same project. If you want, you can check it here.

For a deep understanding, I suggest reading Apple documentation and the WWDC 2019 video when they introduced these topics. You can check the links right below:

See you next time! :)

Start project

Final project

--

--

Roberto Sampaio
My iOS Studies

Servo de Jesus, aprendendo a escrever, desenvolver software, lutar e tocar guitarra. Aprendendo, sempre.