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
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
The following videos are recommended to watch:
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
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):
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
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
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.
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:
- Use UIHostingViewController to host SwiftUI views
- Use UIHostingConfiguration to host SwiftUI views in Cells
- Use observable objects for 2-way data binding