You are currently viewing The Undocumented Facts of Diffable Data Source Section Snapshot

The Undocumented Facts of Diffable Data Source Section Snapshot

The diffable data source section snapshot is an UIKit component introduced in iOS 14. It opens up new possibilities for developers to build various types of hierarchical lists using collection view declaratively.

Even though it is such a powerful component, Apple’s official documentation does not provide much information on how it works and how to use it effectively.

In this article, I would like to share with you some interesting facts about section snapshot that I recently discovered.

With all the being said, let’s get right into it!


The Visual Descriptor

When working with a section snapshot, it is very important to know the hierarchical structure as well as the content of the section snapshot. Therefore, Apple has shipped a visual descriptor alongside the NSDiffableDataSourceSectionSnapshot generic struct.

By leveraging the visual descriptor, we can easily write an ASCII representation of the section snapshot to the Xcode debug console. What’s even better is that it is extremely easy to use, just call the visualDescription()instance method and that’s it!

sectionSnapshot.visualDescription()

The image below shows a collection view constructed with a section snapshot (with 3 root items) and its visual description output in the Xcode console.

Diffable data source section snapshot visual description
Section snapshot visual description

According to Apple documentation, an asterisk (*) represents a visible item, a plus sign (+) represents an expanded item, and a minus sign (-) represents a collapsed item.

What not being mentioned in the documentation is that the visible item represented by an asterisk (*) does not mean that the cell represented by the item is currently visible on the screen.

What do I mean by that?

Let’s say you have a very long list and some of the cells are currently off-screen. All of those items that represent the off-screen cells are considered visible as long as they are not children of a collapsed root item.

Whereas, if a child item is currently on screen, but it is not visible due to its root item is collapsed, then the child item is considered non-visible (refer to “Parent 3” section of the above example).

Another interesting fact we get to know from the visual description is that all child items are collapsed by default. In fact, all items in a section snapshot are collapsed by default, including root items. We can expand items in a section snapshot manually using the following code.

sectionSnapshot.expand([sectionItem])

Automatic Snapshot Creation

This is an interesting fact that I discovered while I was trying to replicate the expandable date picker. It is best to explain using an example.

Let’s say you want to construct a simple expandable list as shown below.

A simple expandable list build with diffable data source section snapshot in Swift
A simple expandable list

Usually, you will first create a snapshot with a main section, and then apply the desire section snapshot to the main section.

// Create snapshot with `main` section, then apply to data source.
var snapshot = NSDiffableDataSourceSnapshot<Section, DataItem>()
snapshot.appendSections([.main])
dataSource.apply(snapshot)

// Create and construct a section snapshot, then apply to `main` section in data source.
var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<DataItem>()
sectionSnapshot.append([parentDataItem1])
sectionSnapshot.append(childDataItemArray1, to: parentDataItem1)
dataSource.apply(sectionSnapshot, to: .main, animatingDifferences: false)

The above code is not wrong, however, there is redundancy within it. Apparently, you can totally skip the snapshot creation part and apply the section snapshot to the data source straightaway.

This is because when you apply a section snapshot to the main section, UIKit will be smart enough to create a snapshot with a main section automatically for you. Thus the above code can be simplified to the following.

// Create and construct a section snapshot, then apply to `main` section in data source.
var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<DataItem>()
sectionSnapshot.append([parentDataItem1])
sectionSnapshot.append(childDataItemArray1, to: parentDataItem1)
dataSource.apply(sectionSnapshot, to: .main, animatingDifferences: false)

With all that being said, there are still reasons where you want to construct the snapshot manually. One reason that I can think of is to have full control over the order of the snapshot’s sections.


Section Snapshot is Always Available

In one of my previous articles, I mentioned that “If you do not have multi-section data within a collection view section, then you do not need to create a NSDiffableDataSourceSectionSnapshot“. Therefore, to construct a simple list as shown below, we will not need a section snapshot.

A simple list build with diffable data source section snapshot in Swift
A simple list

We can construct the list’s data source snapshot using the following code snippet.

var snapshot = NSDiffableDataSourceSnapshot<Section, DataItem>()
snapshot.appendSections([.main])

snapshot.appendItems([
    DataItem.child(Child(title: "Child 1 - A")),
    DataItem.child(Child(title: "Child 1 - B")),
    DataItem.child(Child(title: "Child 1 - C")),
], toSection: .main)

dataSource.apply(snapshot)

As you can see from the code above, a section snapshot is not required, and everything seems to make sense.

But what if I told you that the UICollectionViewDiffableDataSource class has a snapshot(for:) function that returns an NSDiffableDataSourceSectionSnapshot instance? What will we get from this function if we do not apply any section snapshot to the data source?

Let’s find out by adding the following code to the above code snippet.

print(dataSource.snapshot(for: .main).visualDescription())
print("root item count: \(dataSource.snapshot(for: .main).rootItems.count)")

Following is what we get in the Xcode debug console.

Visual description of section snapshot created by UIKit
Visual description of section snapshot created by UIKit

Interestingly, we get a section snapshot with 3 collapsed root items. It seems like UIKit has silently created a section snapshot for us when we append the child items to the main section.

With that, it is safe to say that a section snapshot is always available in a snapshot’s section, we can choose to create it ourselves or let UIKit do the work for us.


The Unwanted Flickering

At the time of writing this article, when you apply a snapshot to the data source, you will see an unwanted flickering glitch. I suspect this might be a bug in the section snapshot. If you are reading this in the future and not able to get the glitch, most probably Apple has fixed it.

Unwanted flickering effect when apply snapshot to data source.
Unwanted flickering effect

It turns out that the flickering is due to the refresh animation. Thus, the most obvious workaround is to disable the animation when applying a snapshot to a data source.

dataSource.apply(snapshot, animatingDifferences: false)

If you would like to keep the animation, another way to work around this is to set the collection view background color to systemBackground.

collectionView.backgroundColor = .systemBackground

Wrapping Up

The diffable data source section snapshot is a fairly new UIKit component and there is still a lot to be explored.

If you have any interesting discoveries about the section snapshot, I would really like to hear from you. Feel free to let me know in the comment section below, or you can reach out to me on Twitter.

Thanks for reading. 👨🏻‍💻


Further Readings


👋🏻 Hey!

While you’re still here, why not check out some of my favorite Mac tools on Setapp? They will definitely help improve your day-to-day productivity. Additionally, doing so will also help support my work.

  • Bartender: Superpower your menu bar and take full control over your menu bar items.
  • CleanShot X: The best screen capture app I’ve ever used.
  • PixelSnap: Measure on-screen elements with ease and precision.
  • iStat Menus: Track CPU, GPU, sensors, and more, all in one convenient tool.