UICollectionView in SwiftUI — reusable component with UICollectionViewCompositionalLayout and UICollectionViewDiffableDataSource Part 1/3

Michał Ziobro
4 min readMay 3, 2020

In WWDC 2019 Apple has presented many new apis and frameworks. We all now about introduction of brand new declarative UI framework SwiftUI. But there were also some new apis targeting UITableView and UICollectionView, i.e. UICollectionViewCompositionalLayout and new diffable datasources like UICollectionViewDiffableDataSource or UITableViewDiffableDataSource. Sadly Apple does not provide its own native CollectionView counterpart in swiftUI. In this article we encompass the power of both this new advances in UICollectionViews and simplicity of building apps layouts with SwiftUI. Our goal is to build lacking reusable component in SwiftUI that wrapps UICollectionView and enables to use its new UICollectionViewCompositionalLayout and underneath implements datasource using brand new UICollectionViewDiffableDataSource.

1. Alternative approach

If you do not want to build your own component from scratch I’ve found interesting very robust ASCollectionView library on GitHub. Nonetheless if you like me prefer to use your own approach and have deeper insight into whats going on underneath I recommend proceed with this tutorial

2. Where you should start to fully understand UICollectionViewCompositionalLayout and UICollectionViewDiffableDataSources

If you want to have really full coverage of this new apis that apple presented in WWDC 2019 you definitely should start from watching two WWDC sessions “Advances in UI Data Sources” and “Advances in Collection View Layout”. Mandatory is also to check Apple sample application Conference-Diffable that demonstrate both this new apis in UIKit.

4. What we build?

At the very first step I would like to demonstrate you what we build.

SwiftUICollectionView component

5. SwiftUICollectionView component basic usage

Here I code snippet that demonstrate what we want to achieve.

As you can see our CollectionView component in SwiftUI should enable following features:

  • defining arbitrary layout including UICollectionViewCompositionalLayout
  • enable to provide sections and items
  • enable to provide snapshots of data for datasource (you see example later on)
  • provide supplementary views like headers, footers
  • enable to specify cell contents using @ViewBuilder and providing different kind of content for different cells
  • enable to define decoration item views and other supplementaries like item badges

6. UIKit implementing generic CollectionViewController that will back SwiftUI

So the first thing we start off with is implementation of generic UIKit view controller i.e. CollectionViewController that will be used underneath by SwiftUI component.

Here we implement generic CollectionViewController and as public properties provide injection points that will be used by SwiftUI counterpart to fill this controller with suitable configurations. We need following input properties:

  • layout of type UICollectionViewLayout
  • snapshot of NSDiffableDataSourceSnapshot<Section, Item>
  • content closure property
  • supplementary view kinds and content closure

Layout will enable to inject whatever collection view layout we want to use. Snapshot is new data representation that will be feed to datasource for being displayed (more on that later). Content will enable providing AnyView content from SwiftUI for each rendered cell.

Here we also define UICollectionViewDiffableDataSource which is new approach of providing datasource as of WWDC 2019 that replaced old-fashioned UICollectionViewDataSource delegate methods.

We also define background queue diffQueue that as it’s name implies will be used when applying new data snapshots to collection view. This way heavy computing data sources won’t freeze our UI.

Next we implement collectionView as lazy var and provide it with layout passed above.

At the end we override viewDidLoad method and

  • configureCollectionView()
  • configureDataSource()
  • reloadDataSource(animating: false)

7. Setup CollectionViewController

Now we need to implement setup code for our collection view and its data source.

Here we add collectionView as subview of controller, then register reusable cell that will host SwiftUI views via UIHostingController. It is custom UICollectionViewCell subclass HostingControllerCollectionViewCell<T>.

Similarly we also register UICollectionReusableViews for supplementary views of different provided kinds and supplementary views for “badge” kind. This in future will enable us to provide header, footers, badges and other supplementary views of any kind.

We also configure UICollectionViewDiffableDataSource with collectionView and cellProvider and supplementaryViewProvider. Cell provider function is equivalent of UICollectionViewDataSource method collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell

8. Item & Supplementary providers

The next step is to implement item and supplementary views provider functions.

Firstly we have cell provider it is equivalent of collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell

Here we just dequeue our reusable cell and host there content (AnyView) provided from SwiftUI component for given indexPath and item.

Secondly we have supplementary views providers. There are two type of providers first for kind == “badge” and second for any kind supplementary view. Badge is rather new feature introduced with UICollectionViewCompositionalLayouts that enables to add supplementary badges in corner of items just like they appear on app icons. They are best for some notification counts to alert the user. Here we implement this badges only if Items have HasBadgeCount protocol implemented and provide non-nil count. Otherwise we display empty view.

For any kind supplementary view we have conentSupplementaryProvider(). It’s provides supplementary views by dequeueing reusable HostingControllerCollectionReusableView<T> which purpose is similar to dequeued cell above, i.e. to host content (AnyView) from SwiftUI for provided kind, indexPath and item params.

9. How to host any content in UIKit cell or supplementary view?

Here we learn how to host AnyView content from SwiftUI inside UIKit UICollectionViewCell or UICollectionReusableView.

We implement subclass of UICollectionViewCell with single method host(). It is generic class but we usually will parametrize it with AnyView later to enable random views passed to CollectionViewController. Here if there is no UIHostingController created yet we need to create it and constraint to cell content. Otherwise we just update rootView of UIHostingController.

Similar code is used for UICollectionReusableView subclass, i.e. HostingControllerCollectionReusableView.

10. Where to go from here?

As this tutorial seems to be getting a bit long. I’ve split it into 3 parts.
1. CollectionViewController in UIKit
2. CollectionView in SwiftUI wrapping CollectionViewController
3. Using CollectionView component in SwiftUI code and compositional layouts definition

Full up to date code you can find on my GitHub
https://github.com/michzio/SwifUICollectionView

--

--