Using SwiftUI inside an ancient UITableView or UICollectionView

Fady Yecob
Just Eat Takeaway-tech
4 min readAug 25, 2022

--

Imagine working on a large and/or old iOS codebase that is using UIKit. That codebase is most likely using a bunch of UITableViews and UICollectionViews, since it’s quite common to display lists or grids of items in an app. But since you probably want to be on the cusp of new technology you most likely no longer want to write code in an imperative way (UIKit). But instead you want to opt for a more declarative way of working (SwiftUI).

You might think that you have to completely rebuild those UITableViews and/or UICollectionViews in that codebase. But that’s not the case since you can combine SwiftUI and UIKit even inside UITableViews and UICollectionViews. There’s even an API that’s recently introduced (during WWDC22) to do this right out of the box.

New struct introduced during WWDC22

A new structure was introduced for iOS 16+, iPadOS 16+, Mac Catalyst 16+ and tvOS 16+ that’s called UIHostingConfiguration. This structure conforms to the UIContentConfiguration protocol that was introduced during WWDC20. During WWDC20, they also introduced UIListContentConfiguration which is used to display an image, title and/or subtitle for simple list layouts. This replaces the standard textLabel, detailTextLabel and imageView properties on UITableViewCell. These are now deprecated. Going forward these are the ways in iOS to use the UIContentConfiguration protocol:

UIContentConfiguration conforming classes/structs are basically a new way to configure UITableViewCells and UICollectionViewCells. So instead of having to create custom subclasses of either UITableViewCell or UICollectionViewCell, you can instead set a custom contentConfiguration property on UITableViewCell and/or UICollectionViewCell.

The code below shows the usage of the default UIListContentConfiguration. We first register a default cell in the viewDidLoad. We then get the default configuration with cell.defaultContentConfiguration() which we then configure with an image, text an image tint color.

Using the older UIListContentConfiguration / UIKit

The code below shows building that same view but using the new UIHostingConfiguration that Apple introduced during WWDC22 instead of the older UIListContentConfiguration. The UIHostingConfiguration initialiser accepts an @ViewBuilder closure. This means we can set it up with any SwiftUI view.

Using the new UIHostingConfiguration / SwiftUI

Both code samples above produce a similar result. With only a small discrepancy in the size of the image as can be seen in the images below:

Result using the older UIListContentConfiguration (UIKit)
Result using the new UIHostingConfiguration (SwiftUI)

Backwards compatibility (e.g iOS 15 & below)

So based on the previous paragraph you might think:

I’m going to close this article since UIHostingConfiguration is iOS 16 and higher only and my app should work on older versions 😡

Don’t close this page just yet. My answer to you would be to:

Create your own version of UIHostingConfiguration

As mentioned in the previous paragraph, we can create our own UIContentConfiguration conforming class/struct which we can use in UITableViewCells and UICollectionViewCells. For that we’ll create a struct called HostingContentConfiguration with two functions to make it conform to the protocol:

This will have the same intialiser format as Apple’s UIHostingConfiguration. It’s a @ViewBuilder closure that returns a generic View. We’ll host this in a UIHostingController of that same generic View type which can be seen below:

Custom UIContentConfiguration conforming struct

Lastly, we’ll need a UIView that conforms to UIContentView. Which only has one requirement: a UIContentConfiguration which is gettable and settable. Luckily we just created that in the code sample above. So then our UIView code can be seen below. The most important stuff happens inside the configure function. This does the following:

The view conforming to UIContentView to use inside UIContentConfiguration

As you might have seen in the code sample above, it has a function called findViewController(). The reason for having this is because we need the parent view controller to add the child hosting controller to. And we don’t want to pass this view controller around in e.g. the initialiser or a property. We’re mimicking Apple’s implementation of UIHostingConfiguration.

The findViewController implementation is quite simple, it uses the next property to recursively find the view controller. Which can be seen below:

Finding the UIViewController for a UIView

And as a final step we can use the code we previously created in our cellForRowAtIndexPath. Depending on the iOS version we’ll use either Apple’s UIHostingConfiguration or our custom implementation. We use the same SwiftUI view in both cases. And for our own custom implementation we add a little bit of padding and height to match Apple’s implementation. This can be seen below:

Using the custom HostingContentConfiguration or Apple’s UIHostingConfiguration

The final result using the custom HostingContentConfiguration looks the same as Apple’s implementation as can be seen below.

The result using our custom HostingContentConfiguration

Conclusion

As you can see Apple’s UIHostingConfiguration structure is quite easy to use. And we can create our own custom implementation of it to add support for iOS 15 and below.

Just Eat Takeaway.com is hiring globally! Want to come work with us? Apply today.

--

--

Fady Yecob
Just Eat Takeaway-tech

iOS Software Development Engineer @ Just Eat Takeaway