Using SwiftUI inside an ancient UITableView or UICollectionView
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:
- UIListContentConfiguration (as previously explained)
- UIHostingConfiguration (for SwiftUI)
- Create your own conforming class/struct
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.
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.
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:
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:
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:
- Get the the UIHostingController from the previous code
- Get the parent view controller
- Add the UIHostingController‘s view to the current view using constraints
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:
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:
The final result using the custom HostingContentConfiguration
looks the same as Apple’s implementation as can be seen below.
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.