📱
UIKit>SwiftUI>UIKit...の入れ子構造でUIHostingControllerに指定する親VC
この記事は TimeTree Advent Calendar 2025 の15日目の記事です。
こんにちは、TimeTreeでiOSアプリ開発をしています pengtaros です🐧
この記事ではUIKit > SwiftUI > UIKit ... のような入れ子構造のビューで発生したオートレイアウトのエラーとその対処方法について書きます
発生したエラーとコード
以下の階層構造を持つビューで発生しました

- SwiftUI.View(背景青)
SomeSwiftUIView.swift
struct SomeSwiftUIView: View {
var body: some View {
Text("1. SwiftUI View")
.padding()
.foregroundStyle(Color.white)
.background(Color.blue)
}
}
- 1をUIHostingControllerを使って表示しているUIKit.UIView(背景緑)
SomeUIViewHasSwiftUIView.swift
final class SomeUIViewHasSwiftUIView: UIView {
private lazy var hostingController = UIHostingController(rootView: SomeSwiftUIView())
init(parent: UIViewController?) {
super.init(frame: .zero)
guard let parent = parent else { return }
let stack = UIStackView()
stack.axis = .vertical
stack.spacing = 10
stack.translatesAutoresizingMaskIntoConstraints = false
stack.backgroundColor = .green
addSubview(stack)
let label = UILabel()
label.text = "2. UIView has SwiftUI View inside"
stack.addArrangedSubview(label)
parent.addChild(hostingController)
stack.addArrangedSubview(hostingController.view)
hostingController.view.backgroundColor = .clear
NSLayoutConstraint.activate([
stack.centerXAnchor.constraint(equalTo: centerXAnchor),
stack.centerYAnchor.constraint(equalTo: centerYAnchor)
])
hostingController.didMove(toParent: parent)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
- 2をUIViewRepresentableを使って表示しているSwiftUI.View(背景黄)
SwiftUIViewHasUIRepresentable.swift
struct SwiftUIViewHasUIRepresentable: View {
weak var parent: UIViewController?
var body: some View {
VStack {
Text("3. SwiftUI has UIKit View inside")
SomeUIViewHasSwiftUIViewRepresentable(parent: parent)
}
.frame(width: 260, height: 120)
.background(Color.yellow)
}
}
struct SomeUIViewHasSwiftUIViewRepresentable: UIViewRepresentable {
weak var parent: UIViewController?
func makeUIView(context: Context) -> SomeUIViewHasSwiftUIView {
SomeUIViewHasSwiftUIView(parent: parent)
}
func updateUIView(_ uiView: SomeUIViewHasSwiftUIView, context: Context) {}
}
- 3をUIHostingControllerを使って表示しているルートとなるUIViewController(背景白)
RootViewController.swift
final class RootViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let stack = UIStackView()
stack.axis = .vertical
stack.spacing = 10
stack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stack)
let label = UILabel()
label.text = "4. UIViewController has SwiftUI View inside"
stack.addArrangedSubview(label)
let hostingController = UIHostingController(rootView: SwiftUIViewHasUIRepresentable(parent: self))
addChild(hostingController)
view.addSubview(hostingController.view)
let hostingView = hostingController.view!
stack.addArrangedSubview(hostingView)
NSLayoutConstraint.activate([
stack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
hostingController.didMove(toParent: self)
}
}
発生したエラー
*** Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency', reason: 'child view controller:<_TtGC7SwiftUI19UIHostingControllerV26uikit_swiftui_millefeuille15SomeSwiftUIView_: 0x107854c00> should have parent view controller:<_TtGC7SwiftUI19UIHostingControllerV26uikit_swiftui_millefeuille29SwiftUIViewHasUIRepresentable_: 0x110034000> but actual parent is:<uikit_swiftui_millefeuille.RootViewController: 0x10500d770>'
terminating due to uncaught exception of type NSException
要約すると、1. SwiftUI.View (背景青) をUIView上に表示するためのUIHostingControllerに指定された親VCが、実際の親VCと異なっているようです。
問題の部分
SomeUIViewHasSwiftUIView.swift
// 背景緑のビュー
final class SomeUIViewHasSwiftUIView: UIView {
// 背景青のSwiftUI.ViewをUIViewで表示するためのUIHostingController
private lazy var hostingController = UIHostingController(rootView: SomeSwiftUIView())
init(parent: UIViewController?) {
~~
guard let parent = parent else { return }
// 親となるVCを引数で受け取ってセット
parent.addChild(hostingController)
~~
}
}
RootViewController.swift
// 背景白のビュー
final class RootViewController: UIViewController {
override func viewDidLoad() {
~~
// ここでself(RootViewController)を親として渡しているが、実際に親となっているのはこのインスタンス(UIHostingController<SwiftUIViewHasUIRepresentable>)自身
let hostingController = UIHostingController(rootView: SwiftUIViewHasUIRepresentable(parent: self))
~~
}
エラーの解決方法
rootViewはUIHostingControllerインスタンス化後にセットすることが可能です。
you can change that view later using the rootView property.
UIHostingControllerインスタンスを先に作ってrootViewにparentとして渡し、その後rootViewをUIHostingControllerにセットすることでエラーを解消できます
RootViewController.swift
final class RootViewController: UIViewController {
override func viewDidLoad() {
~~
let hostingController = UIHostingController<SwiftUIViewHasUIRepresentable?>(rootView: nil)
let rootView = SwiftUIViewHasUIRepresentable(parent: hostingController)
hostingController.rootView = rootView
addChild(hostingController)
~~
}
}
(余談)この問題に遭遇した背景
TimeTreeはサービス開始から10年が経っています。
歴史がある巨大なコードベースを元にサービスを早く・安定してデリバリーしていくために
- SwiftUIで新規開発・刷新する部分
- SwiftUI登場以前にUIKitで作成した既存の資産を再利用する部分
を開発の目的に合わせて選択しています。その中でどうしてもUIKit>SwiftUI>UIKit...のような入れ子構造なる部分が出てきてしまいこのようなエラーに遭遇しました。
TimeTreeのエンジニアによる記事です。メンバーのインタビューはこちらで発信中! note.com/timetree_inc/m/m4735531db852
Discussion