1. なぜ2026年にUIKitなのか

Appleは毎年のWWDCでSwiftUIの進化を強調しており、宣言的UIの利便性はもはや疑いようがない。

しかし、このアプリには譲れない要件がある。それはTime-to-Text(起動からテキスト入力可能になるまでの時間)を500ms以下にすることだ。

SwiftUIでは、@StateObjectの初期化、bodyの再評価、.onAppearのタイミング制御など、宣言的UIフレームワーク固有のオーバーヘッドが存在する。これらは一般的なアプリでは無視できる程度だが、ミリ秒単位の最適化を追求する場面では無視できない。

一方、UIKitのviewDidAppearからbecomeFirstResponder()を呼ぶパスは決定論的に高速だ。ViewControllerのライフサイクルは明確で、いつキーボードが表示されるかを正確に制御できる。

設計判断:Time-to-Text 500ms以下という非機能要件を満たすため、UIKitを採用。

2. Storyboardを完全に排除した理由

UIKitを選んだだけでは不十分だ。Storyboardにも起動パフォーマンスに影響するオーバーヘッドがある。

  • XML解析コスト:Storyboardは内部的にXMLファイルであり、起動時にパースが必要
  • Segue解決コスト:画面遷移のためのSegue設定の解決に時間がかかる
  • 不必要な複雑性:シンプルなメモアプリにStoryboardの視覚的設計ツールは過剰

代わりに、SceneDelegateで直接ViewControllerを生成する方式を採用した。

func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
           options connectionOptions: UIScene.ConnectionOptions) {
    PerformanceLogger.shared.signpostBegin(name: "SceneConnect")
    guard let windowScene = (scene as? UIWindowScene) else { return }
    let window = UIWindow(windowScene: windowScene)
    let composeVC = ComposeViewController()
    let navController = UINavigationController(rootViewController: composeVC)
    window.rootViewController = navController
    window.makeKeyAndVisible()
    PerformanceLogger.shared.signpostEnd(name: "SceneConnect")
}

このアプローチにより、Storyboardのパース時間ゼロ、Segue解決時間ゼロを実現している。SceneDelegateから直接ComposeViewControllerを生成することで、起動パスから不要な処理を完全に排除した。

3. viewDidAppearの1行がすべてを完結させる

UIKitの最大の強みは、ViewControllerのライフサイクルが明確に定義されていることだ。viewDidAppear(_:)が呼ばれた時点で、ビューは完全に画面に表示されている。この確実なタイミングでbecomeFirstResponder()を呼ぶことで、キーボード表示までの遅延を最小化できる。

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    activateTextInput()
}

private func activateTextInput() {
    textView.becomeFirstResponder()
    PerformanceLogger.shared.endTimeToText()
}

os_signpostによる計測をナノ秒精度で実施している。PerformanceLogger.shared.endTimeToText()がSignpostの終了イベントを記録し、Instrumentsでの詳細な分析を可能にしている。

計測結果:目標は500ms以下。アイコンのタップからの様子を画面録画で測った記録(2026年8月11日、iPhone 16e・ウォーム起動)では、入力欄へのフォーカスが0.17秒、キーボードの表示完了が0.4秒でした。

4. パフォーマンス計測の仕組み

「速い」という感覚的な判断ではなく、定量的なデータに基づいて設計判断を行っている。

  • PerformanceLogger:os_signpostをラップしたカスタムロガー。起動・送信・UI更新の各フェーズを計測
  • Instruments連携:Xcodeのos_signpost Instrumentでナノ秒精度のトラッキングが可能
  • 主要メトリクス:Time-to-Text(起動→入力可能)、Send-to-Reset(送信ボタン→UI初期化)
  • 継続的モニタリング:開発中のビルドすべてでパフォーマンス計測を実施し、リグレッションを即座に検出

パフォーマンスは機能である。ユーザーは「速さ」を明示的に要求しなくても、遅いアプリは無意識のうちに使わなくなる。だからこそ、パフォーマンスを数値で管理し続けることが重要だ。

5. SwiftUIを使う場面はあるか

UIKit一択というわけではない。アプリ内には、SwiftUIが適している場面もある。

  • 設定画面:起動速度に影響しない非クリティカルな画面ではSwiftUIの宣言的UIが生産性を高める
  • クリティカルパスの定義:起動 → メモ入力 → 送信というコアフローはUIKitで維持する
  • ハイブリッドの複雑性:UIKitとSwiftUIを混在させると、UIHostingControllerの管理やデータフローの一貫性に注意が必要
  • 現時点の判断:メモアプリのUIはシンプルであり、全画面UIKitで統一するほうがコードベースの一貫性が保たれる

将来的にSwiftUIのパフォーマンスがさらに改善されれば再評価の余地はあるが、クリティカルパスのUIKitは当面維持する方針だ。

よくある質問

Q. SwiftUIでは500ms以下の起動は無理ですか?
不可能ではない。ただ、SwiftUIでは@StateObjectの初期化やbodyの再評価の分だけ処理が加わる。最速を狙うこの開発では、ライフサイクルを細かく制御できるUIKitを選んだ(両者を同じ条件で計測して比べた結果ではない)。
Q. Storyboardを使わないデメリットは?
UIの可視化がコードベースになる。Interface Builderでのドラッグ&ドロップ設計ができなくなる。ただしメモアプリのUIはシンプルなので、コードベースのUI構築で問題にならない。
Q. 実機での計測結果は?
目標は500ms以下。記録の残っている計測は、アイコンのタップからの様子を画面録画で測ったもの(2026年8月11日、iPhone 16e・ウォーム起動)で、入力欄へのフォーカスが0.17秒、キーボードの表示完了が0.4秒。開発中はos_signpostで区間を記録し、Instrumentsで分析している。
Q. 今後SwiftUIに移行する予定は?
クリティカルパス(起動→入力→送信)はUIKitを維持する。設定画面などの非クリティカルな画面については、SwiftUIの採用を検討する余地はある。