UIKit vs SwiftUI:起動速度のためにUIKitを選んだ理由
1. なぜ2026年にUIKitなのか
2026年現在、新規iOSアプリの多くはSwiftUIで開発されている。Appleも毎年のWWDCでSwiftUIの進化を強調し、宣言的UIの利便性はもはや疑いようがない。
しかし、このアプリには譲れない要件がある。それはTime-to-Text(起動からテキスト入力可能になるまでの時間)を500ms以下にすることだ。
SwiftUIでは、@StateObjectの初期化、bodyの再評価、.onAppearのタイミング制御など、宣言的UIフレームワーク固有のオーバーヘッドが存在する。これらは一般的なアプリでは無視できる程度だが、ミリ秒単位の最適化を追求する場面では無視できない。
一方、UIKitのviewDidAppearからbecomeFirstResponder()を呼ぶパスは決定論的に高速だ。ViewControllerのライフサイクルは明確で、いつキーボードが表示されるかを正確に制御できる。
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での詳細な分析を可能にしている。
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は当面維持する方針だ。
よくある質問
@StateObject初期化やbody再評価のオーバーヘッドがある。最速を追求するなら、UIKitの決定論的なライフサイクルが有利。os_signpostでナノ秒単位で計測し、Instrumentsで詳細分析を実施している。参考文献
- Apple Developer — UIKit Documentation — Obsidian連携シンプルメモのUI構築に採用したフレームワークの公式リファレンス
- Apple Developer — SwiftUI Documentation — 比較検討した宣言的UIフレームワーク。パフォーマンス測定の対象
- Apple Developer — os_signpost / OSSignposter — Time-to-Text計測に使用したInstruments連携のパフォーマンス測定API
- WWDC 2023 — Analyze hangs with Instruments — UIレスポンス最適化の参考にしたWWDCセッション