SwiftUIのUIHostingControllerがもつ_UIHostingViewのプロパティ


SwiftUIを調べているうちに、システム側で行っている処理が気になったので内部のClassについて調査しました。

今回調査したのは`UIHostingController`の動作についてです。

## `UIHostingController`は`_UIHostingView`をもつ

名前からある程度よそう可能ですが、`UIHostingController`は`_UIHostingView`を`self.view`として保持しています。

### _UIHostingView

```swift
class _UIHostinView: UIView {
    var _rootView : ContentView
    var viewGraph : ViewGraph
    var viewUpdater : ViewUpdater
    var eventBindingManager : EventBindingManager
    var preferenceBridge : PreferenceBridge?
    var currentTimestamp : Time
    var needsRootViewUpdate : Bool
    var needsEnvironmentUpdate : Bool
    var updateDepth : Int
    var isRendering : Bool
    var postUpdateActions : Array<() -> ()>
    var disabledBackgroundColor : Bool
    var allowFrameChanges : Bool
    var disableSafeArea : Bool
    var safeAreaInsetsMask : Set
    var previousSafeAreaInsets : UIEdgeInsets
    var environmentOverride : EnvironmentValues?
    var viewController : UIViewController?
    var isAppRoot : Bool
    var layoutTraitsDidChangeCallback : (_LayoutTraits, _LayoutTraits) -> ()?
    var gestureRecognizer : UIKitGestureRecognizer
    var displayLink : DisplayLink?
    var lastRenderTime : Time
    var needsDisplayLinkUpdate : Bool
    var canAdvanceTimeAutomatically : Bool
    var pendingPreferencesUpdate : Bool
    var nextTimerTime : Time?
    var updateTimer : NSTimer?
    var skipped : Int
    var colorScheme : ColorScheme?
    var invertLayoutDirection : Bool
    var navigationBridge : UIKitNavigationBridge<ContentView>
    var alertBridge : AlertBridge<ContentView, Presentation>
    var actionSheetBridge : AlertBridge<ContentView, Presentation>
    var sheetBridge : SheetBridge<ContentView>
    var popoverBridge : UIKitPopoverBridge<ContentView>
    var statusBarHidden : Bool
    var contextMenuBridge : ContextMenuBridge<ContentView>?
    var accessibilityEnabled : Bool
    var accessibilityNodes : Array<Any>
    var accessibilityNeedsUpdate : Bool
    var scrollTest : ScrollTest?
    var delegate : UIHostingViewDelegate?
}

```

この中にある`scrollTest`非常に気になります。

## `Bridge`に注目

`_UIHostingView`は、`Bridge`を保持していました。

```swift
    var preferenceBridge : PreferenceBridge?
    var navigationBridge : UIKitNavigationBridge<ContentView>
    var alertBridge : AlertBridge<ContentView, Presentation>
    var actionSheetBridge : AlertBridge<ContentView, Presentation>
    var sheetBridge : SheetBridge<ContentView>
    var popoverBridge : UIKitPopoverBridge<ContentView>
    var contextMenuBridge : ContextMenuBridge<ContentView>?
```

`PreferenceBridge`をおいて、そのほかはViewの遷移に必要なインスタンスを保持しているようです。


### `Bridge`について調べてみた

#### navigationBridge

```swift
class UIKitNavigationBridge<ContentView> {
    var host : _UIHostingView<ContentView>?
    var hasNavigationItem : Bool
    var activePresentation : BridgedPresentation?
    var destinations : Value<Dictionary<ViewIdentity, NavigationDestinationContent>>
    var lastEnvironment : EnvironmentValues
    var tableViewRegistry : TableViewRegistry
    var isNavigationBarHidden : Bool
    var containingNavControllerOverride : UINavigationController?
}
```

#### alertBridge

```swift
class AlertBridge<ContentView, Presentation> {
    var host : _UIHostingView<ContentView>?
    var isShown : Bool
    var seed : VersionSeed
    var alertController : UIAlertController?
    var lastEnvironment : EnvironmentValues
    var lastPresentation : Presentation?
    var style : UIAlertControllerStyle
}
```

#### actionSheetBridge

```swift
class AlertBridge<ContentView, Presentation> {
    var host : _UIHostingView<ContentView>?
    var isShown : Bool
    var seed : VersionSeed
    var alertController : UIAlertController
    var lastEnvironment : EnvironmentValues
    var lastPresentation : Presentation?
    var style : UIAlertControllerStyle
}
```

#### sheetBridge

```swift
class SheetBridge<ContentView> {
    var host : _UIHostingView<ContentView>?
    var seed : VersionSeed
    var isShown : Bool
    var presentedVC : SheetHostingController<AnyView>?
    var lastPresentation : SheetPreference?
    var lastEnvironment : EnvironmentValues
}
```

#### popoverBridge

```swift
class UIKitPopoverBridge<ContentView> {
    var host : _UIHostingView<ContentView>?
    var isShown : Bool
    var seed : VersionSeed
    var presentedVC : UIHostingController<AnyView>?
    var lastPresentation : PopoverPresentation?
}
```

## `EnvironmentValues`

`lastEnvironment`ならアクセスできそうなので中身をみてみた

```swift
[
    EnvironmentPropertyKey<TableViewRegistryKey> = Optional(SwiftUI.TableViewRegistry),
    EnvironmentPropertyKey<NavigationStateKey> = nil,
    EnvironmentPropertyKey<PresentationModeKey> = Binding<PresentationMode>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.LocationBox<SwiftUI.FunctionalLocation<SwiftUI.PresentationMode>>, _value: SwiftUI.PresentationMode(isPresented: false)),
    EnvironmentPropertyKey<ActiveContextMenuKey> = ViewIdentity(seed: 0),
    EnvironmentPropertyKey<ColorSchemeKey> = light,
    EnvironmentPropertyKey<AccentColorKey> = Optional(#007AFFFF),
    EnvironmentPropertyKey<UndoManagerKey> = Optional(<NSUndoManager: 0x60000243ac10>),
    EnvironmentPropertyKey<AccessibilityInvertColorsKey> = false,
    EnvironmentPropertyKey<AccessibilityReduceMotionKey> = false,
    EnvironmentPropertyKey<AccessibilityReduceTransparencyKey> = false,
    EnvironmentPropertyKey<AccessibilityDifferentiateWithoutColorKey> = false,
    EnvironmentPropertyKey<BackgroundInfoKey> = BackgroundInfo(layer: 0, groupCount: 0),
    EnvironmentPropertyKey<VerticalUserInterfaceSizeClassKey> = Optional(SwiftUI.UserInterfaceSizeClass.regular),
    EnvironmentPropertyKey<HorizontalUserInterfaceSizeClassKey> = Optional(SwiftUI.UserInterfaceSizeClass.compact),
    EnvironmentPropertyKey<DisplayScaleKey> = 3.0,
    EnvironmentPropertyKey<ColorSchemeContrastKey> = standard,
    EnvironmentPropertyKey<LegibilityWeightKey> = Optional(SwiftUI.LegibilityWeight.regular),
    EnvironmentPropertyKey<PreferredContentSizeCategoryKey> = large, EnvironmentPropertyKey<LayoutDirectionKey> = leftToRight,
    EnvironmentPropertyKey<LocaleKey> = en (current)
]
```