iOS開発でTDDするための準備

## はじめに
iOS開発でTDDする際に準備しておくと良いことを記載しました。
個人的な備忘録も兼ねているので分かりづらい部分あるかもしれません。
気が向いたら内容をもう少し充実させたり、サンプルプロジェクトを追加したりしようと思います。

## Main Interfaceを削除
Main Interfaceを削除しておきます。
デフォルトはMain.Storyboardが設定されています。
<img width="677" alt="スクリーンショット 2018-04-24 22.53.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/106954/c3b18a82-535f-9dd1-cbe9-eff26171a35b.png">

## AppDelegateの設定
上記でMain Interfaceを削除してしまった代わりに、コードでwindowの`rootViewController`を指定します。

```swift:AppDelegate.swift
import UIKit

@UIApplicationMain
final class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = SetUpVC() // 任意のViewController
        window?.makeKeyAndVisible()
        
        return true
    }
}
```

## main.swiftの独自実装
main.swiftを準備します。
main関数を実行するためにAppDelegateから`@UIApplicationMain`を削除しておきます。

```swift:main.swift
// Xcode10
UIApplicationMain(CommandLine.argc,
                  CommandLine.unsafeArgv,
                  NSStringFromClass(UIApplication.self),
                  NSStringFromClass(AppDelegate.self))

// Xcode8 or 9
UIApplicationMain(CommandLine.argc,
                  UnsafeMutableRawPointer(CommandLine.unsafeArgv)
                    .bindMemory(
                        to: UnsafeMutablePointer<Int8>.self,
                        capacity: Int(CommandLine.argc)),
                  NSStringFromClass(UIApplication.self),
                  NSStringFromClass(AppDelegate.self))
```
追記：Xcode10では、第二引数を`UnsafeMutableRawPointer`を利用せず記述できるようになりました。
（@yukkobay さん情報提供ありがとうございます）

## テスト用AppDelegateを作成
テストビルド時に起動するAppDelegateを準備します。

```swift:TestAppDelegate.swift
import UIKit

final class TestAppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    
    func applicationDidFinishLaunching(_ application: UIApplication) {
        
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = UIViewController() // 任意のViewController
        window?.makeKeyAndVisible()
    }
}
```

## 環境変数を設定し、参照できるようにする
Build Schemeに応じて任意のAppDelegateを起動するための前準備として環境変数の設定します。

ここでは上記で作成したTestAppDelegateを起動するために、テストビルドのArgumentsの設定に環境変数を追加しています。

1. Product > Scheme > Edit Scheme を選択
2. Environment Variables の項目で任意の Key と Value を設定

<img width="856" alt="スクリーンショット 2018-04-24 23.15.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/106954/35790d72-bb74-62f8-e7ae-d52949130469.png">


以下のコードでValueの値を参照することができます。

```
ProcessInfo.processInfo.environment["設定したKey"]
```

よしなに利用できるようにラッパーを用意すると良さそうです。

```swift:Global.swift
struct Global {
    static var isUnitTest: Bool {
        return ProcessInfo.processInfo.environment["isUnitTest"] == "true"
    }
}
```

## main.swiftの修正
```swift:main.swift
import UIKit

private var appDelegateName: String {
    if Global.isUnitTest {
        return NSStringFromClass(TestAppDelegate.self)
    } else {
        return NSStringFromClass(AppDelegate.self)
    }
}

UIApplicationMain(CommandLine.argc,
                  UnsafeMutableRawPointer(CommandLine.unsafeArgv)
                    .bindMemory(
                        to: UnsafeMutablePointer<Int8>.self,
                        capacity: Int(CommandLine.argc)),
                  NSStringFromClass(UIApplication.self),
                  appDelegateName)
```

これでBuild Schemeに応じて任意のAppDelegateを起動できるように修正できました。

## 環境
* Xcode 9.2
* Swift 4.0

## 参考
* [ios開発でTDDをやるために最低限やっておくこと](https://qiita.com/ko2ic/items/5979c97b4ac589b1c8a1) [@ko2ic](https://qiita.com/ko2ic)

---
[^1]: ここでは、正しいラベルのタイトルやボタンが表示されているか、正しく画面遷移できているか、などのテストのこととします。