4 min read

Use SwiftUI With UIKit - WWDC22

How to bridge data between the legacy app and SwiftUI back and forth. How to embed SwiftUI into UICollectionView and UITableView with UIHostingConfiguration. Here is the summary for the "Use SwiftUI With UIKit" session from WWDC22.

This is what the SwiftUI sandwich looks like. UIHostingController is a view controller that renders SwiftUI content on its UIView and manages all the SwiftUI Views lifecycle.

In iOS16 new self-sizing options for UIHostingViewController were added, allowing it to be added as a child view controller as we did with plain old view controllers.

Bridging Data from App to SwiftUI

There are at least two ways to update the views hosted inside UIHostingViewController.


The first one is manual:

The second way to keep the UI in sync is to use ObservableObjects.
First, we make our model to ObservableObject protocol and pass it to SwiftUI view as a dependency:

Then we pass it to the SwiftUI view when creating a host view controller:

SwiftUI in Cells

UIHostingConfiguration

The brand new UIHostingConfiguration feature allows to host SwiftUI views inside UICollectionView and UITableVIewCells:

Looks like somewhere under the hood it creates and reuses UIHostingViewController with sizingOptions assigned to .preferredContentSize.

The following videos are recommended to watch:

Margins

An important point is how UIKit cells with embedded SwiftUI views handle margins for its content. There is some default value applied, but allows to configure it by using margins(...) modifier:

Background

There is also a way to paint the background with (quite unexpectedly) background modifier:

Here is how the background modifier affects the cell (quite expectedly):

Swipes

SwiftUI cells hosting configuration also allows handling swipes. We were warned 100 times to avoid using IndexPath in handlers because it will lead to a mess due to the cells' reusable nature. That's what we knew before, didn't we?

Configuration update handler

Here is an example of how to use configurationUdpateHandler with UIHostingConfiguration:

Cells Data flow

Apple's recommendation is to use DiffableDataSource only for handling collection updates like insertion, deletion, and item moves by using items' ids.

For individual items updates handling, they recommend using ObservableObject.

This is all quite understandable.

It's possible to use UICollectionViewDataSource not only with items' ids but to treat the entire item as a Hashable thing. This will allow not only to handle collection-wise updates but also individual items refresh. In that case, the changed item would be actually treated as deleted and inserted back.

And this is a very reliable way to handle collections in sync with the UI simply because there is no place for mistakes: you always show what you have in the collection.

The downside of this solution is that it causes the cell to reload completely. To be more exact, it triggers the cell to deletion and re-insert.

When using a pure UIKit cell that was affordable, at least I've never faced any issues with that. However, with cells backed with SwiftUI, it looks like not. Anyway, this is expectable, because every UIHostingConfiguration cell is likely to have an entire UIHostingViewController under the hood.

Hopefully, a brand new A17 Bionic chip would be capable to handle that, lol.

Self-sizing cells

The most wanted feature since iOS8 is still with us.

Another recommendation to watch or read:

Bridging Data from SwiftUI to UIKit

The provided way to handle mutation made in SwiftUI is simply ObservableObject's 2-ways binding:

Key Takeaways

  • Use UIHostingViewController to host SwiftUI views
  • Use UIHostingConfiguration to host SwiftUI views in Cells
  • Use observable objects for 2-way data binding

References

Use SwiftUI with UIKit