SwiftUI 4泊5日の旅（2日目：プロジェクト構成）

# はじめに

[前回](https://qiita.com/knagauchi/items/a6e5ff3d26e80ce88ac9)は[SwiftUI Tutorials](https://developer.apple.com/tutorials/swiftui/tutorials)の冒頭に沿った内容でしたが、今回はプロジェクト構成の話です。
前回いじった`ContentView`は一体どこから呼ばれてたのか？

# SceneDelegateで最初の画面を出す
その答えは`SceneDelegate`です。
<img width="565" alt="スクリーンショット 2020-05-03 23.35.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277107/f7f74f3e-7330-fa11-c018-e39e81cd499e.png">
まず、`Info.plist`の`Application Scene Manifest`にて、`SceneDelegate`がデフォルトで指定されています。
<img width="565" alt="スクリーンショット 2020-05-03 22.25.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277107/448095c0-c7e0-7a9b-3742-47d91941ea0f.png">
このためアプリ起動時、`SceneDelegate`の以下部分が呼ばれます。

```Swift:SceneDelegate.swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) 
    {
        let contentView = ContentView()
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }
    •••
}
```
`rootViewController `に`ContentView`を与えているので、最初の画面が表示されます。

# UIHostingControllerを用いてUIKitとSwiftUIを繋ぐ
`UIHostingController`は、SwiftUI用の新しいUIViewControllerです。
つまり、これを従来の`UIWindow`に渡すことで、UIKitからSwiftUIに繋いでいるわけです。

また、`UIHostingController`を継承したカスタムクラスを用意して、

```Swift:MyHostingController.swift
import SwiftUI

class MyHostingController: UIHostingController<ContentView> {
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder, rootView: ContentView())
    }
}

```

このように`ContentView`の指定をカスタムクラス内で行えば、外からは普通のControllerに見えるので、Storyboardから表示させることもできます。iOS 12以前で使ったらクラッシュしますけどね。

# UIViewRepresentableを用いてSwiftUIからUIKitに繋ぐ
逆にSwiftUI側でUIKitのパーツを使うこともできます。例えばUITextViewを使いたい場合、

```Swift:TextView.swift
struct TextView: UIViewRepresentable {
    @Binding var text: String

    func makeUIView(context: Context) -> UITextView {
        return UITextView()
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.text = text
    }
}
```
こんなクラスを作って、生成時と更新時の処理を書きます。利用側では、

```Swift:ContentView.swift
struct ContentView: View {
    @State private var message = "Hello, World!"
    var body: some View {
        TextView(text: $message)
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
    }
}
```
のように書きます。`message`には`@State`が付いてるので、データが変更されたら表示に即反映されます。

# PreviewProviderでプレビュー可能にする
`ContentView`には、デフォルトで以下の記述があります。

```Swift:ContentView.swift
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```
ここにプレビューしたいViewを書いておくと、Xcodeが自動で見つけてプレビュー対象にしてくれます。

# プロジェクト構成まとめ
SwiftUIに必要なものは以下です。
・`Info.plist`の`Application Scene Manifest`定義
・`SceneDelegate`
・`UIHostingController`の`rootView`に渡すView
・プレビューしたい場合`PreviewProvider`

# 最後に

[次回](https://qiita.com/knagauchi/items/90fbf8a32926881e1b12)はSwiftUIで簡単に実装できる何らかのレイアウトを紹介します。
従来と比べてどれくらい簡単になるかを体験しましょう！

で、1日目すっかり忘れてたんですが、これは「4泊5日のGW旅行」なので、グルメ写真も載せます。
<img width="480" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277107/af793db5-3946-012c-09f2-91f59ebb6d38.jpeg">
お昼は松島町の「さんとり茶屋」さんで穴子丼をいただきました😋
<img width="480" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277107/132e5da7-c4de-630b-d53f-ec1ff9b8df37.jpeg">
夜は旅行の定番、懐石料理。さぁ乾杯！！
※写真は2017年GW旅行のものです