NSDiffableDataSourceSectionSnapshot with more than 80 sections causes UI hangs

I am trying to create this UI using a diffable data source.

I have everything working, however, we experience a severe UI hang, between 2200-3000ms, if there are more than 80 sections. It doesn't matter how many items are in the sections, just that there are over 80.

Here is the setup for the snapshots.

var snapshot = Snapshot()
snapshot.appendSections(sections.map(\.branch))
apply(snapshot, animatingDifferences: animatingDifferences)

sections.forEach { section in
    var sectionSnapshot = SectionSnapshot()

    if let currentBuild = section.builds.first.map({ SectionItem.currentBuild($0) }) {
        sectionSnapshot.append([currentBuild])
    }

    if section.builds.count > 1 {
        let additionalBuildsHeader = SectionItem.additionalBuildsHeader(forSection: section.branch)
        sectionSnapshot.append([additionalBuildsHeader])
        let additionalBuilds = section.builds
                .dropFirst()
                .map({ SectionItem.additionalBuild($0) })
        sectionSnapshot.append(additionalBuilds, to: additionalBuildsHeader)

        if expandedSections.contains(additionalBuildsHeader) {
            sectionSnapshot.expand([additionalBuildsHeader])
        }
    }
    apply(sectionSnapshot, to: section.branch, animatingDifferences: animatingDifferences)
}

I tried to do this with a single data source snapshot, instead of using the section snapshots and then the UI does not experience any hangs. While this works, we don't get the nice collapsible sections and all builds are displayed at the same level.

Here is the code for that setup.

var snapShot = Snapshot()
snapShot.appendSections(sections.map(\.branch))

sections.forEach { section in

    if let currentBuild = section.builds.first.map({ SectionItem.currentBuild($0) }) {
        snapShot.appendItems([currentBuild], toSection: section.branch)
    }

    if section.builds.count > 1 {
        let additionalBuilds = section.builds
                .dropFirst()
                .map({ SectionItem.additionalBuild($0) })

        snapShot.appendItems(additionalBuilds, toSection: section.branch)
    }
}

apply(snapShot, animatingDifferences: animatingDifferences)

Am I doing something wrong here with this setup in using the section snapshots? It seems like the hangs might be due to a layout pass being triggered on each apply, but I don't know any other way to build the section snapshots.

What is the type of section.builds here? Maybe I'm reading this wrong, but in lines like:

    let currentBuild = section.builds.first.map({ SectionItem.currentBuild($0) })

it looks like currentBuild is going to be an array of SectionItems, so your data source item type is an array, which seems … a bit unusual.

Hi @Polyphonic,

So, section.builds is an array. We have an array of all available builds. The first element in this array is the current build, and I want to highlight it as such. In that line it is grabbing the first item from the builds array and then mapping that to the SectionItem type. currentBuild itself is just a single item and not an array. I decided to restructure the Section type to remove this confusion.

struct Section: Hashable {
    public let branch: String
    public let currentBuild: Build
    public let additionalBuilds: [Build]
}

// Updated snapshot code for the above type
var snapshot = Snapshot()
snapshot.appendSections(sections.map(\.branch))
apply(snapshot, animatingDifferences: animatingDifferences)

sections.forEach { section in
    var sectionSnapshot = SectionSnapshot()

    let currentBuild = SectionItem.currentBuild(section.currentBuild)
    sectionSnapshot.append([currentBuild])

    if section.additionalBuilds.isEmpty == false {
        let additionalBuildsHeader = SectionItem.additionalBuildsHeader(forSection: section.branch)
        sectionSnapshot.append([additionalBuildsHeader])
        let additionalBuilds = section.additionalBuilds.map({ SectionItem.additionalBuild($0) })
        sectionSnapshot.append(additionalBuilds, to: additionalBuildsHeader)

        if expandedSections.contains(additionalBuildsHeader) {
            sectionSnapshot.expand([additionalBuildsHeader])
        }
    }
    apply(sectionSnapshot, to: section.branch, animatingDifferences: animatingDifferences)
}

The issue is still present, however.

NSDiffableDataSourceSectionSnapshot with more than 80 sections causes UI hangs
 
 
Q