SwiftUI.ViewとUIHostingControllerの初期化順序の調整

UIHostingControllerを使ってSwiftUI.Viewを表示する時に、Viewの引数に渡すクロージャーの中でViewControllerを使いたいことが稀によくある(本当に?)。

素直に考えるとこうなる。

// 順序的にvcを使えない
let view = FooView(onTap: {
    // vcで何かしたい
})
let vc = UIHostingController(rootView: view)

少し頭を捻るとこうなる。

let view = FooView()
let vc = UIHostingController(rootView: view)
vc.rootView.onTap = { [weak vc] in ... }

UIHostingController.rootViewpublic var rootView: Content なので、rootViewのプロパティを変更することはできる。だけどViewのプロパティを自分で書き換えるのはなんだかしっくりこない。

rootViewがvarということは別のことが考えられる。

let vc = UIHostingController<FooView?>(rootView: nil)
let view = FooView(onTap: { [weak vc] in ... })
vc.rootView = view

rootViewをnilで初期化しておき、後で差し替える作戦。 open class UIHostingController<Content> : UIViewController where Content : View という定義に対してnilを渡せるのか?という気持ちに一瞬なるが、これはOptional(とNever)がconditonal conformanceでViewに準拠しているのでvalidである。

extension Optional : View where Wrapped : View {
    public typealias Body = Never
}

という感じで、rootViewにnilを渡すことによる遅延初期化を上手く使っていきましょうというお話でした。