SwiftUI の List や ForEach でデータを表示する際、各要素を一意に識別するための仕組みが必要です。Identifiable プロトコルと id パラメータについて解説します。
なぜ識別子が必要なのか
SwiftUI はリストの各要素を追跡し、データが変更されたときに効率的に UI を更新します。この追跡には一意の識別子が必要です。識別子がないと、要素の追加・削除・並び替え時に正しくアニメーションできなかったり、パフォーマンスが低下したりします。
id パラメータによる識別
最もシンプルな方法は、ForEach や List に id パラメータを渡すことです。
struct ContentView: View { let fruits = ["りんご", "みかん", "バナナ"] var body: some View { List(fruits, id: \.self) { fruit in Text(fruit) } } }
id: \.self は要素そのものを識別子として使うことを意味します。String や Int など Hashable に準拠した型で使えますが、重複する値があると問題が発生します。
// 危険な例:重複する値がある let items = ["A", "B", "A", "C"] // "A" が重複 List(items, id: \.self) { item in Text(item) }
重複があると SwiftUI は同じ要素として扱ってしまい、意図しない動作の原因になります。
Identifiable プロトコル
カスタム型を使う場合は、Identifiable プロトコルに準拠させるのがベストプラクティスです。
struct Task: Identifiable { let id = UUID() var title: String var isCompleted: Bool }
Identifiable に準拠するには、id プロパティを実装するだけです。UUID を使えば自動的に一意の値が生成されます。
struct TaskListView: View { let tasks = [ Task(title: "買い物", isCompleted: false), Task(title: "掃除", isCompleted: true), Task(title: "洗濯", isCompleted: false) ] var body: some View { List(tasks) { task in HStack { Text(task.title) Spacer() if task.isCompleted { Image(systemName: "checkmark") } } } } }
Identifiable に準拠した型を使う場合、id: パラメータは省略できます。
既存のプロパティを id として使う
データベースから取得したデータなど、すでに一意の ID を持っている場合は、それを使うこともできます。
struct User: Identifiable { let id: Int // データベースの主キー var name: String var email: String }
新規に作成するデータで、永続化の必要がない場合に便利。自動的に一意な値が生成される。
データベースや API から取得したデータで、すでに一意の ID が割り当てられている場合に使用。
id にキーパスを指定
Identifiable に準拠していない型でも、id パラメータにキーパスを指定すれば使えます。
struct Item { let code: String var name: String } struct ContentView: View { let items = [ Item(code: "001", name: "商品A"), Item(code: "002", name: "商品B"), Item(code: "003", name: "商品C") ] var body: some View { List(items, id: \.code) { item in Text(item.name) } } }
id: \.code で code プロパティを識別子として使用しています。ただし、可能であれば Identifiable プロトコルに準拠させる方が Swift らしい書き方です。