メンタルモデルの転換
UIKitでは、ナビゲーションは命令的です。システムに正確に何をすべきか指示します:
let detailVC = DetailViewController()
detailVC.item = selectedItem
navigationController?.pushViewController(detailVC, animated: true)ビューコントローラを作成し、設定し、スタックにプッシュします。アクションを制御しているのはあなたです。
SwiftUIは異なるアプローチを取ります。ナビゲーションするのではなく、データを新しいビューに提示します。すべてがデータ駆動です。ビューを制御するのではなく、データを制御します。
データ駆動ナビゲーションの実践
NavigationStackでは、ナビゲーションは状態によって駆動されます。どのデータがどのビューにマッピングされるかを宣言すると、SwiftUIがトランジションを処理します:
struct ContentView: View {
@State private var path: [Item] = []
var body: some View {
NavigationStack(path: $path) {
List(items) { item in
Button(item.name) {
path.append(item) // Modify data, not views
}
}
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
}
}
}
}重要な行はpath.append(item)です。ビューをプッシュしているのではなく、配列にデータを追加しているのです。SwiftUIが変更を監視し、対応するビューを自動的に表示します。
なぜこれが重要なのか
この違いは哲学的なだけでなく、実用的な影響があります。ナビゲーションが状態であるため、正しいパス配列を構築することでディープリンクが無料で手に入ります。パスを保存することでナビゲーション状態を永続化・復元できます。複数のアイテムを一度に追加することで、プログラム的に任意の深さまでナビゲートできます。
また、画面を閉じることはデータの削除に過ぎないということも意味します。path.removeLast()を呼ぶとトップビューがポップされます。配列をクリアするとルートに戻ります。ビューコントローラの参照を追跡したり、ナビゲーション階層をたどったりする必要はありません。
この転換を内面化するには時間がかかります。特にUIKitの経験が長い場合はなおさらです。しかし一度理解できると、SwiftUIのナビゲーションははるかに予測可能になります。ビューはデータの純粋な関数となり、ナビゲーションはそのデータのもう一つのピースに過ぎなくなります。
