How do you put SwiftUI inside a UIKit app, and what do you need to watch for?
Predict first
Does a hosting controller have a view and a natural size?
import UIKit
import SwiftUI
let host = UIHostingController(rootView: Text("Hello"))
let size = host.sizeThatFits(in: CGSize(width: 300, height: 300))
print(host.view != nil, size.width > 0 && size.width < 300, size.height > 0)
- true true true
- true false false
- false false false
Mental model. A hosting controller is an adapter plug: UIKit sees an ordinary view controller, and SwiftUI sees an ordinary parent.
Wrap a SwiftUI view in a UIHostingController and use it as any other view controller: push it, present it, put it in a tab or embed it as a child with the normal containment calls. Its view exists and can be added to a hierarchy, and it can report its natural size through sizeThatFits(in:), so Auto Layout can size it. For smaller pieces, UIHostingConfiguration hosts SwiftUI as the content of a collection or table cell without a controller per cell, and in recent SDKs hosting controllers can size themselves with sizingOptions so they work as self-sizing content.
The design question is who owns the state. Pass the SwiftUI view a model object (an @Observable class or an ObservableObject) that the UIKit side also holds, so both stay in sync, rather than copying values in and hoping. Use closures or the model for events flowing back out (onSave), and avoid hosting a navigation stack inside a UIKit navigation controller unless you want two bars. Treat the hosted view like a child: make sure the hosting controller is added with addChild, addSubview, didMove, or lifecycle and trait events will not reach it. Measure performance in cells, since each hosted cell has a real cost.
The hosting controller reports the SwiftUI content’s ideal size, here a small box around the text.
Common traps
- Adding only the hosting controller’s view and skipping containment. Appearance and trait events then do not arrive.
- Copying values into the SwiftUI view and mutating them in UIKit. The two drift apart. Share a model.
They might follow up with
- How does a SwiftUI view in a cell get sized?
UIHostingConfigurationlets the cell size from the SwiftUI content. - How does the hosted view dismiss itself? Through a closure or the model, or the environment’s dismiss action when presented.
Related questions
- 7.9.2 How do UIViewRepresentable and its coordinator work, and when does SwiftUI call each method?
- 7.3.2 What is view controller containment, and what is the correct way to embed one controller in another?
- 7.6.2 Table view or collection view: which do you choose for new code?
How do UIViewRepresentable and its coordinator work, and when does… →