UIKitのUIViewController/UIViewをSwiftUIで利用する場合の利用方法とその詳細

# はじめに

UIKitのUIViewController/UIViewをSwiftUIで使う場合の方法とその詳細について書いておきます。

大抵コード書いてるときに検索してから要点が見つかるまで時間がかかるため先に結論をテンプレートとして示します。

# 結論: このテンプレートを埋める

UIViewControllerを利用したい場合は、下記コードを加筆修正しいけば最低限動くレベルになるはずです。

```swift
import SwiftUI
import UIKit

struct SampleViewController: UIViewControllerRepresentable {
    // 1. 利用したいViewControllerにエイリアスをつける（もちろんつけずに下記コードで都度書いてもいい）
    typealias UIViewControllerType = UINavigationController

    // 2. 必須のメソッド。作成したいViewControllerを返すメソッドを実装する
    func makeUIViewController(context: Context) -> UIViewControllerType {
        
    }

    // 3. 必須のメソッド。Viewが更新された場合に必要な処理を実装する
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {

    }

    // 4. Coordinatorのファクトリーメソッドを実装する。
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    // 5. Coordinatorを定義する
    class Coordinator {

    }
}
```

UIViewについてはだいたい一緒なので省略。

## 説明

1. 利用したいViewControllerをtypealiasで指定します。ただし、ここで書かずにメソッド`makeUIViewController(context:)`と`updateUIViewController(context:)`で明示すればいいだけです。私はtypealias使うほうが（設計開発時には試行錯誤する間は）楽だと感じてます
    - このサンプルでは`UINavigationController`にしていますがそれは自由です
1. `makeUIViewController(context:)`はViewControllerを最初に作成する1回きり呼び出されます。たいてい表示される初回のみのはずです
    - Swiftはファクトリメソッドを`make~`ではじめよ、というスタイルをガイドで示しておりそれに従った命名となっています
    - 引数のcontextからは後述するCoordinatorのインスタンスが取り出せます
    - https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable/makeuiviewcontroller(context:)
1. `updateUIViewController(_:context:)`はSwiftUIから更新が必要になった場合に呼び出されます。
    - https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable/updateuiviewcontroller(_:context:)
1. 必須では有りませんが、後述するCoordinatorを利用する場合にそれを作成するメソッドであり、`makeUIViewController(context:)`実行前に1度のみ呼び出されます
1. Coordinatorを定義します。Coordinatorは利用するViewControllerがイベントを処理するために、そのイベントハンドリングを行う型を定義することができます。

# Appleのチュートリアル

AppleはSwiftUIによるアプリ開発チュートリアルを公開していて、UIViewControllerRepresentableについても利用例が示されています。
https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit

チュートリアルの完成コードを図で説明すると次のような感じ

![スクリーンショット 2021-08-16 19.54.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/589bad8d-c886-75b9-a105-14045c2519f4.png)

チュートリアルでは左右のスワイプジェスチャーでページングする機能を持つ、UIPageViewControllerを利用し、独自のPageViewControllerをSwiftUI.Viewとしています。

![pageviewcontroller.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/7edb3ae6-9abd-051a-baaa-1287eeba5ea1.png)


- `struct PageViewController: UIViewControllerRepresentable`
    - やってること
        - UIViewControllerRepresentableに準拠させて作る
        - currentPageを`@Binding`として変化を外部に伝える
    - 気になるポイント
        - SwiftUI.Viewにも準拠してるんだけど名前は~ViewControllerでいい
            - ~Viewとかにしてもと思ういいけど
        - CoordinatorにはparentとしてPageViewControllerを保持する
            - makeCordinatorする際に引数でselfを渡す
                - Coordinator側からPageViewControllerのプロパティにアクセスしている（できてしまうとも言うが...）

慣れないうちは（もしくは慣れても）ややこしいと感じる部分かもしれませんが

- 自作するのはPageViewController
    - SwiftUI.Viewプロトコルに準拠している
- 利用するのはUIPageViewController
    - UIKitのUIPageViewControllerの表示とデリゲートを利用している

## どういうときにUIViewControllerRepresentableとUIViewRepresentableを使い分けるか

Appleのチュートリアルから察するに、利用するUIKitのコンポーネントがUIViewControllerならUIViewControllerRepresentable、UIViewならUIViewRepresentableにすればいいんじゃないかと思います。


# その他

## TCAだと

このチュートリアルでは`@Binding`を使って結果を共有しています。これが[TCA](https://github.com/pointfreeco/swift-composable-architecture)だったら`struct PageViewController: UIViewControllerRepresentable`にStoreをもたせて`make〜`でviewStateに対して処理を行ってもかまわないし、そうではなく自作するViewController内にStoreをバケツリレーするだけでもかまわないでしょうね。

## Context

`makeUIViewController(context:)`から取り出せるContextは`typealias Context = UIViewControllerRepresentableContext<Self>`です。

https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable/context

`UIViewControllerRepresentableContext`はCoordinator以外にSwiftUI.EnvironmentValuesも取り出すことができます。

https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentablecontext

こういうながったらしい型をtypealiasでContextとするのはいいですね。