SwiftUIを触って分かったこと：①初期画面の設定方法

下記ブログの転載です！
https://rc-code.info/ios/post-293/

`SwiftUI` をいじっていて、**初回起動時の画面設定の仕方が今までと異なっていた**ので、ざっと紹介します。

`SwiftUI` を利用するか否かに限らず、アプリのUIを複数インスタンスで管理する方法（すなわち複数画面を単体アプリで管理する事と同意）を提供するために、`Xcode11` から `UIScene` という概念が導入されましたので、そのことに少し触れようと思います。
詳細は [こちら](https://developer.apple.com/documentation/uikit/app_and_scenes?changes=latest_minor) 。


## 今までの設定方法

今までは `AppDelegate` の `didFinishLaunchingWithOptions` 内で `window` を生成する、もしくは `Deployment Info` の `Main Interface` にて設定するのが一般的でした。

**&#x2b07;&#xfe0f;AppDelegate didFinishLaunchingWithOptions の例**

```
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        UNUserNotificationCenter.current().delegate = self
        
        window = UIWindow(frame: UIScreen.main.bounds)
        if let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateInitialViewController() {
            let navigationVc = UINavigationController(rootViewController: vc)
            self.window?.rootViewController = navigationVc
        }
	}
    以下省略
}
```

**&#x2b07;&#xfe0f;Main Interface への設定例**
<a href="https://rc-code.info/wp-content/uploads/2019/06/06-09%2016.11.53.png"><img src="https://rc-code.info/wp-content/uploads/2019/06/06-09%2016.11.53.png" alt="Main Interface 設定画面" class="size-medium" /></a>


## 新しい初期画面の設定方法

`Xcode11` 導入後は `UIWindowSceneDelegate` を利用した下記の方法が推奨されるようです。

### 1. UISceneConfiguration の設定 

まず、`AppDelegate` に新しく追加された `configurationForConnecting` 関数にて、`UISceneConfiguration` として設定する対象を明記します。

```
class AppDelegate: UIResponder, UIApplicationDelegate {

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }
		他省略
}
```

上記で設定した `Default Configuration` は `Info.plist` の `Application Scene Manifest` に記載された `Scene Configuration > Application Session Role > Item0` が当て込まれます。
下記が `Info.plist` の画像です。

<a href="https://rc-code.info/wp-content/uploads/2019/06/06-09%2017.11.26.png"><img src="https://rc-code.info/wp-content/uploads/2019/06/06-09%2017.11.26.png" alt="Application Scene Manifest" class="size-medium" /></a>

この `Item0` 内で `Configuration Name` を `Default Configuration` に設定しているので、上記のコードを記載した時にこの `Item0` が `UISceneConfiguration` として呼び出されるわけです。

`Item0` には `LaunchStoryboard Name` という項目もあり、名前の通りここに記載した名前の `Storyboard` が 起動時の画面として呼び出されます。
また、`Delegate Class Name` に指定した名前のクラスが `SceneDelegate (UIWindowSceneDelegate)` として利用されます。

### 2. UIWindowSceneDelegate の設定

`SceneDelegate` では、今まで `AppDelegate` の `didFinishLaunchingWithOptions` 関数で指定していたように、`window` への割り当てができます。
そうです。
`UIApplicationDelegate` 同様、`UIWindowSceneDelegate` も `window` のオプショナル定義を持っているのですw

```
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.rootViewController = UIHostingController(rootView: ContentView())
        self.window = window
        window.makeKeyAndVisible()
    }
		以下省略
}
```

上記の `ContentView` は `SwiftUI` の `View` プロトコルを満たした構造体です。
`UIHostingController` は描画のライフサイクルを担当する、いわば  `UIViewController` の `Controller` 部分だけを抽出し `SwiftUI` に合わせた感じのクラスのようです。（ざっくりとしか見てないので雑ですいませんw）

上記の例では、この `UIHostingController` を `window` の `rootViewController` として格納しています。

さて、これで `ContentView` がメインの `View` 構造体として設定されたので、あとはこの中身をゴリゴリと変えてく、ということになります。

ちなみに、ここまで説明してきた諸々の設定は、プロジェクト作成時に `SwiftUI` 項目にチェックを入れて開始するとデフォルトで入っています。
つまり、`SwiftUI` ではこの駆動方法が一般的ということになりそうですね！


## 今までのやり方はダメなの！？

冒頭に記載した今まで通りの初期画面設定ですが、`Xcode11` でも `UIApplicationDelegate` プロトコルから `var window` のオプショナル定義は削除されておらず、引き続きこちらでも駆動はできるようです。
`Main Interface` の指定枠も同様です。

ですが、`UIApplicationDelegate` が端末とアプリケーションを繋ぐためのデリゲートを担当し、アプリの複数画面描画（とりわけ `window` や `Scene`）を `SceneDelegate` が担うという構造になっていきそうな予感はプンプンしますね。
そのうち `AppDelegate` から `window` 関連の制約が `deprecated` になる気がします。


## 思っちゃったこと

`Xcode11` から `Window` や `Scene` を管理するための機構が多く見られます。
個人的な見解ですが、これは「個人が発信する」という文化にアプリケーションをマッチさせるための布石のように思います。

今回（2019/6 現在）の `WWDC` での発表を見ると、`iPad` を利用したプログラミング開発を斡旋するような機能や、`PC` と連動させて `iPad` を活用するような試みが見られます。
これは比較的大きな端末サイズに民衆を慣れさせることに成功した `Apple` が、次はその端末でもう少し上位の体験を簡単に実行できるようにするフェーズに入ったことの証明の様に感じます。
`iPadOS` だけではなく `iOS` でも同様の動きを取っていくでしょう。

現状、私たちは動画をアップする際、まずカメラで撮影し、編集し、それをSNSにアップロードします。コメントが来ればコメント欄を開き、いいねを押されればその人の画面に遷移してお返しをします。

これをシームレスに単体の端末内で実行できるとすれば、多くの民衆、もしくはプロのカメラマンや動画プロデューサーでさえ、その端末を利用するでしょう。

画面内に「カメラ自体のキャプチャ、編集枠、配信中の画像（編集を加えたキャプチャ）、コメント欄、Info枠...etc」などが混在し、それを直感的に掌の中で実行できる様になることを目指しているのではないでしょうか？
そのための布石としての `UIWindowSceneDelegate` である気がします。

そのうち、リジェクトの内容で
「ここのコメント画面は動画を見ながら確認できる様に別シーンで管理してくれたまえよ」
といった内容も出てくるのではなかろうかと。