[iOS]Storyboardを使わずにアプリを初期化する

皆さんはStoryboardを使わずにアプリをiOSアプリを開発したことはありますか？

今までRubyやReactなどのWeb系の言語を使って開発してきた僕にとってはGUIを使ってアプリを構築することに少し違和感があったと同時に、全部コードで実装したいと思っていました。
Storyboardを使わずにアプリを初期化することで、Interface Builderが裏で行っている事、Interface Builder及びStoryboardのありがたみに気付く事が出来たのでまとめます。

# 対象読者
- Storyboardを使った開発はある程度慣れてきた人

# バージョン
Swift 5.3
Xcode 12.2
iOS 14.2

# 事前知識
Storyboard無しで開発する上で、アプリの描画周りのクラスについて知っておいたほうが良いと思うので軽く説明しておきます。

## UIView
UIViewはUIを配置するベースとなる要素です。また画面に配置するすべてのUI(ボタン、ラベルなど)が継承するクラスです。
プロジェクトを初期化すると生成されるViewControllerは、UIViewControllerを継承したクラスであり、UIViewControllerはUIViewを一つ持っています。

## UIWindow
UIWindowはアプリのUIを配置するための土台のような物で、アプリが必ず一つは持っている要素です。パソコンのウィンドウと同じに認識でいいと思います。


## UIScene
アプリのUIを表示する土台です。

Sceneに関してはこちらの記事がとても参考になりました。
[iOS13のSceneDelegate周りのアプリの起動シーケンス - Qiita](https://qiita.com/omochimetaru/items/31df103ef98a9d84ae6b)

## UIScreen
デバイスのディスプレイの大きさなど、ハードウェア視点から見たデバイスの状態を管理するクラスです。

## まとめると
関係を整理すると、
`UIScreen > UIScene > UIWindow > UIView > ボタン、ラベルなどのUIパーツ`
という関係になると思います。(間違ってたらご指摘ください。)


# 実装
では実際にStoryboardなしの開発を始めてみましょう。

## Storyboardと別れを告げる
いつもどおりプロジェクトを作ったら、まずは長い間お世話になったStoryboardに別れを告げましょう。
`Main.storyboard`と削除します。そして、プロジェクトの一番上を選択し、`Deployment Info`の`Main Interface`の部分が`Main`となっているはずなので、その部分を削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352602/3794e2e3-36e2-21c0-c3d1-cba0a240fe00.png)

次にinfo.plistを開き、` Application Scene Manifest > Scene Configuration > Item 0 > Storyboard Name`の行を消します。(⌘ + Delete)で消せます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352602/592c73de-d6a9-7d24-5f43-3543acc1d73d.png)
これで完全にStoryboardと別れを告げることが出来ました。ここで起動してみると真っ黒な画面が表示されると思います。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352602/cf8ad7e2-7c1c-eb32-0641-5de7144e3460.png" width="300px" alt="属性">
これはStoryboardを消したことによって、Storyboardが裏でやっていたUIwindowにUIViewを乗せるという作業がされなくなったためです。

## UIWindowにUIViewを乗せる
Storyboardが勝手にやってくれていた作業を実装しましょう。実装するためにはアプリのシーン周りの状態を管理するSceneDelegate.swiftを以下のように編集します。

```swift:SceneDelegate.swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
	
	var window: UIWindow?

	// アプリの起動時に呼ばれる関数
	// アプリに接続されるシーンオブジェクト(scene)を引数として受け取ります。
	func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
		//渡されてきたsceneを元にUIWindowSceneを初期化(UIWindowSceneはUISceneを継承したクラスです)
		guard let windowScene = (scene as? UIWindowScene) else { return }
		
		// UIWindowをディスプレイの大きさに指定して初期化
		window = UIWindow(frame: UIScreen.main.bounds)

		// ViewControllerを初期化
		let viewController = ViewController()
		
		// windowのコントローラーにViewControllerのインスタンスを設定
		window?.rootViewController = viewController
		
		// windowを最前面に表示！
		window?.makeKeyAndVisible()
		
		// windowが表示されるシーンオブジェクトとして、アプリに接続されているシーンオブジェクトを設定
		window?.windowScene = windowScene
	}
        // 中略
```
ここで、アプリを一度起動してみると、以前と変わらず真っ黒な画面が表示されると思います。なぜならviewに色が設定されていないからです。(Storyboardが初期化時に勝手に設定してくれていたということですね！)なので、viewの背景色を白に設定するコードを追加します。

```swift:viewController.swift
import UIKit

class ViewController: UIViewController {
	override func viewDidLoad() {
		super.viewDidLoad()
        // viewはUIViewのインスタンス
		view.backgroundColor = .white
	}
}
```

ここで、アプリを起動すると、プロジェクトの初期化直後のように真っ白な画面が表示されたら成功です。
# 終わりに
Storyboardを使わずにアプリを初期化することが出来ました。Storyboardという得体のしれない概念が何をしていたかが分かったと同時に、裏でよしなにしてくれていたか事が分かりました。
(こちらの記事はSwiftを触り始めた赤ちゃんが書いています。間違っている部分を発見された場合は、コメント欄などでご指摘いただけると幸いです。)


# 参考文献

- [UIScreenとUIWindowの違い - Qiita](https://qiita.com/st43/items/eb97455b3853fbb3d07d)
- [Part 1 — Creating iOS Apps Programmatically! | by Dhruvik Chevli | Hashtag by IECSE | Medium](https://medium.com/iecse-hashtag/swift-part-1-creating-ios-apps-programmatically-fb32aa71fe34)
- [How To Build iOS UIs Programmatically | by Kai Koh | Better Programming | Medium](https://medium.com/better-programming/how-to-build-ios-uis-programmatically-e0c4be2c3f24)
- [Creating Apps Without Storyboards in iOS 13 | by Corey Davis | Better Programming | Medium](https://medium.com/better-programming/creating-apps-without-storyboards-in-ios-13-fc9550bb9c12)
- [Windows and Screens | Apple Developer Documentation](https://developer.apple.com/documentation/uikit/windows_and_screens)
- [iOSアプリの基本構造 - Qiita](https://qiita.com/st43/items/81c7f9ae79126a01ae51)
- [iOS13のSceneDelegate周りのアプリの起動シーケンス - Qiita](https://qiita.com/omochimetaru/items/31df103ef98a9d84ae6b)
- [UIScene | Apple Developer Documentation](https://developer.apple.com/documentation/uikit/uiscene)
- [UIWindowScene | Apple Developer Documentation](https://developer.apple.com/documentation/uikit/uiwindowscene)
- [UIWindow | Apple Developer Documentation](https://developer.apple.com/documentation/uikit/uiwindow)