コンテンツへスキップ

SwiftUIナビゲーション:Viewではなくデータを渡す

UIKitの命令的なナビゲーションからSwiftUIのデータ駆動ナビゲーションへのメンタルモデルの転換を理解する。

メンタルモデルの転換

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のナビゲーションははるかに予測可能になります。ビューはデータの純粋な関数となり、ナビゲーションはそのデータのもう一つのピースに過ぎなくなります。

参考になりましたか?BlueskyMastodonでフォローして、Swiftのヒントやインディー開発の最新情報をチェックしてください。