【Xcode11】Storyboardを使わずコードだけで画面を生成する方法

#目的
Xcode11でStoryboardを使わずにコードだけで画面を作ろうと思ったときに`Could not find a storyboard named 'Main' in bundle NSBundle`というエラーがずっと出ていてだいぶ困ったので忘れないように残しておこうと思います。
気が付いてみたらだいぶ簡単なエラーだったのですが、もし同じところで行き詰まっている人がいれば参考にしてください！笑

#Storyboardを使わずに画面を生成する方法(Xcode11より前)
Xcode11より前のStoryboardを使わずにコードだけで画面を生成するときには、
1.Main.storyboardを削除。
2.プロジェクトのTARGETS > Infoにある`Main storyboard file base name`という項目を削除。
<img width="865" alt="スクリーンショット 2019-10-22 0.27.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474449/944c1857-5d25-6bac-7f68-e8da88233bf4.png">

3.AppDelegate.swiftで初回の起動画面を設定。

```swift:AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = ViewController()
        window?.makeKeyAndVisible()

        return true
    }
}
```

これで初回起動時に表示される画面がViewController.swiftになると思います。

#Storyboardを使わずに画面を生成する方法(Xcode11の場合)
Xcode11でプロジェクトを作成するとAppDelegate.swiftだけではなくSceneDelegate.swiftというものも一緒に生成されます。
Xcode１１ではSceneDelegate.swiftでもStoryboardを使わないように設定しないといけません。
1.Main.storyboardを削除。
2.プロジェクトのTARGETS > Infoにある`Main storyboard file base name`という項目を削除。
3.プロジェクトのTARGETS > Infoにある`Application Scene Manifest` > `Scene Configuration` > `Application Session Role` > `Item0(Default Configura...` > `Storyboard Name`を削除。（私はここでもMain.storyboardを指定していることに気がつかずにずっとエラーが出ていました！笑）
<img width="865" alt="スクリーンショット 2019-10-22 0.44.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474449/625206e6-dc50-00f8-447b-d927f0efe42e.png">
4.AppDelegate.swiftで初回の起動画面を設定。(Xcode11より前と同じ)

5.SceneDelegate.swiftで初回の起動画面を設定。

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

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let scene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: scene)
        window?.rootViewController = ViewController()
        window?.makeKeyAndVisible()
    }
}
```

これでXcode11でもStoryboardを使わずに画面を生成することができます。
#iOS13以前のバージョンにも対応させる方法
iOS13以降だけでなく古いバージョンのOSにも対応させた方がいいと思うので、iOS13以前のバージョンにも対応させる方法です。
簡単にいうと、iOS13以降ではアプリ起動時にSceneDelegate.swiftが呼ばれて、iOS13以前ではAppDelegate.swiftが呼ばれるみたいなので各メソッドで`@availble(iOS 13.0, *)`を呼べばいいだけのようです。

```swift:AppDelegate.swift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        if #available(iOS 13.0, *) {

        } else {
            //iOS13以前のときだけ呼ばれる
            window = UIWindow(frame: UIScreen.main.bounds)
            window?.rootViewController = ViewController()
            window?.makeKeyAndVisible()
        }

        return true
    }

    // MARK: UISceneSession Lifecycle

    @available(iOS 13.0, *)
    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    @available(iOS 13.0, *)
    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }


}
```

```swift:SceneDelegate.swift
@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let scene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: scene)
        window?.rootViewController = ViewController()
        window?.makeKeyAndVisible()
    }
    func sceneDidDisconnect(_ scene: UIScene) {...}

    func sceneDidBecomeActive(_ scene: UIScene) {...}

    func sceneWillResignActive(_ scene: UIScene) {...}

    func sceneWillEnterForeground(_ scene: UIScene) {...}

    func sceneDidEnterBackground(_ scene: UIScene) {...}

}
```

#参考にした記事
[Storyboard 抜きで、コードオンリーで iOS アプリの UI を作る](https://qiita.com/lovee/items/5e8c7752d7e383660543)
[Xcode11で作成したプロジェクトを古いOSに対応させる(とりあえず版)](https://qiita.com/Hackenbacker/items/86e73f34b377ca415838)
[Xcode11で作成したプロジェクトを古いOSに対応させる(完結編)](https://qiita.com/Hackenbacker/items/9cf9dfb4df66a51cd46a)
[iOS13のSceneDelegate周りのアプリの起動シーケンス](https://qiita.com/omochimetaru/items/31df103ef98a9d84ae6b)

#さいごに
とても簡単なことですが`Could not find a storyboard named 'Main' in bundle NSBundle`というエラーで行き詰まってから気がつくまでだいぶ時間がかかったので同じところで困ってる人に役に立ったら幸いです！
もし間違っているところなどありましたら教えていただけると嬉しいです！