iOS9でアプリが起動直後にクラッシュする場合の回避方法


#はじめに

とあるアプリをiOS9 beta3にて動作確認をしていたところ、起動直後にフリーズしてしまい、数秒後にクラッシュする現象が起こっていました。

動作確認環境

* iOS9 beta3
* Xcode7.0 beta3


エラーログを確認したところ下記のエラーがでていました。

```
Application windows are expected to have a root view controller at the end of application launch
```

```
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Application windows are expected to have a root view controller at the end of application launch'
*** First throw call stack:
(
	0   CoreFoundation                      0x000000010b9cf7c5 __exceptionPreprocess + 165
	1   libobjc.A.dylib                     0x000000010afdadcd objc_exception_throw + 48
	2   CoreFoundation                      0x000000010b9cf62a +[NSException raise:format:arguments:] + 106
	3   Foundation                          0x000000010ac14bf1 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 198
	4   UIKit                               0x00000001097c3eca -[UIApplication _runWithMainScene:transitionContext:completion:] + 2875
	5   UIKit                               0x00000001097c1208 -[UIApplication workspaceDidEndTransaction:] + 188
	6   FrontBoardServices                  0x000000010fee1c0f -[FBSSerialQueue _performNext] + 192
	7   FrontBoardServices                  0x000000010fee1f7d -[FBSSerialQueue _performNextFromRunLoopSource] + 45
	8   CoreFoundation                      0x000000010b8f9ba1 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
	9   CoreFoundation                      0x000000010b8efacc __CFRunLoopDoSources0 + 556
	10  CoreFoundation                      0x000000010b8eef83 __CFRunLoopRun + 867
	11  CoreFoundation                      0x000000010b8ee998 CFRunLoopRunSpecific + 488
	12  UIKit                               0x00000001097c0ba5 -[UIApplication _run] + 402
	13  UIKit                               0x00000001097c52cb UIApplicationMain + 171
	14  TestApp                             0x0000000108968d8f main + 111
	15  libdyld.dylib                       0x000000010d4e292d start + 1
	16  ???                                 0x0000000000000001 0x0 + 1
)
```

アプリ起動時にwindowにrootViewがセットされていないとの事ですが、
iOS8以前は動作していた事を考えるとiOS9からここら辺の挙動が変更されているみたいです。
(beta版のため、製品版では発生しない可能性はあります)

実装を見てみると下記のようになっていました。

```objectivec:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
	// ウィンドウ初期化
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [self.window makeKeyAndVisible];

    return YES;
}
```

windowのrootViewControllerがセットされていませんね。
どうやらこのアプリはアプリ起動時に初回起動かどうかを判定してrootViewControllerを出し分けている処理が別にありましたが、
blocksを挟んだ非同期での処理だったため、アプリ起動時にrootViewControllerが存在しない状態になっていました。

#対応方法

対応方法でもなんでもないですが、windowのrootViewControllerを正しくセットすれば動作しました。
(とりあえずで空のViewControllerをセット)

```objectivec:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
	// ウィンドウ初期化
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.rootViewController = [UIViewController new];
    [self.window makeKeyAndVisible];

    return YES;
}
```

あとは下記のような実装でもクラッシュする原因となってしまいますので、rootViewControllerに正しくセットするようにしましょう。

```objectivec:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
	// ウィンドウ初期化
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    UIViewController *viewController = [UIViewController new];
    [window addSubview:viewController.view];
    [self.window makeKeyAndVisible];

    return YES;
}
```

* [“Application windows are expected to have a root view controller at the end of application launch” error when running a project with Xcode 7, iOS 9 - stackoverflow](http://stackoverflow.com/questions/30884896/application-windows-are-expected-to-have-a-root-view-controller-at-the-end-of-a)

#終わりに

アプリは正しく実装しましょうというAppleさんの意思を垣間見た気がしました。
(いや、言われなくても正しい実装を心がけましょうw)

詳しくは調べていないですが、Xcode6.xでビルドしたアプリをiOS9で実行した場合でもクラッシュしたので、beta版の不具合の可能性もありますが、今後のアップデートで確認していこうと思います。