KeyPathの基礎
プロパティを第一級の値として参照します。
「KeyPathの基礎」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全4レッスンが含まれています。
キーパスとは
キーパスは、実際にアクセスすることなく、プロパティへの経路を表す値です。読み取る対象のインスタンスとは切り離して、読み取りたいプロパティを保存し、再利用できる参照だと考えてください。
キーパスは、バックスラッシュ、型名、ドット、プロパティ名の順に記述します。Swiftはこの式を KeyPath 値として扱うため、ほかの値と同じように渡せます。
struct Person {
let name: String
let age: Int
}
let nameKeyPath = \Person.name
print(type(of: nameKeyPath))値を読み取る
キーパスを通してプロパティを読み取るには、インスタンスに対して [keyPath:] サブスクリプトを使います。角括弧内にキーパスを渡すと、Swiftがその経路の値を返します。
これはプロパティ名を直接記述する処理に対応する実行時の仕組みです。ただし、ここではプロパティをハードコードするのではなく、データによって選択できます。
struct Person {
let name: String
let age: Int
}
let alice = Person(name: "Alice", age: 30)
let path = \Person.name
print(alice[keyPath: path])キーパス構文の詳細
基本形は、バックスラッシュのエスケープ、ルート型、ドット、プロパティ名の順です。バックスラッシュは、プロパティの値ではなくキーパスを指定することをSwiftに知らせます。
ルート型は、キーパスの起点となる型です。結果型は、最後のプロパティの型です。したがって、Person からその age プロパティへのキーパスでは、ルート型が Person、値の型が Int になります。
struct Person {
let name: String
let age: Int
}
let ageKeyPath: KeyPath<Person, Int> = \Person.age
let bob = Person(name: "Bob", age: 42)
print(bob[keyPath: ageKeyPath])ネストされたプロパティをたどる
キーパスを使って、ネストされた値の内部までたどれます。プロパティ自体が構造体の場合は、さらにドットを続けて、より深いプロパティに到達できます。
チェーン全体は1つのキーパス値であり、ルート型と結果型はそのチェーンの開始地点と終点を表します。
struct Address {
let city: String
}
struct Person {
let name: String
let address: Address
}
let cityPath = \Person.address.city
let carol = Person(name: "Carol", address: Address(city: "Oslo"))
print(carol[keyPath: cityPath])キーパスを変数に保存する
キーパスは単なる値なので、変数に保存したり、関数から返したり、キーパスのコレクションとして保持したりできます。これにより、実行時に読み取るプロパティを決定できます。
ここでは、フラグに基づいて保存しておいた2つのキーパスのどちらかを選択しています。
struct Person {
let name: String
let age: Int
}
let showAge = true
let chosen: PartialKeyPath<Person> = showAge ? \Person.age : \Person.name
let dave = Person(name: "Dave", age: 27)
print(dave[keyPath: chosen])PartialKeyPathとAnyKeyPath
Swiftには、キーパスを表す型がいくつかあります。KeyPath<Root, Value> は両端の型を把握します。PartialKeyPath<Root> はルート型を把握しますが、値の型は消去されます。AnyKeyPath は両方の型を消去します。
型情報を多く保持するほど、コンパイラーがより多くの検査を行えます。値の型が異なるキーパスを本当に混在させる必要がある場合にだけ、部分型や任意型を使ってください。
struct Person {
let name: String
let age: Int
}
let paths: [PartialKeyPath<Person>] = [\.name, \.age]
let eve = Person(name: "Eve", age: 19)
for p in paths {
print(eve[keyPath: p])
}型が既知の場合の省略記法
コンテキストからルート型がすでに明らかな場合は、型名を省略し、バックスラッシュ、ドット、プロパティだけを記述できます。ルート型はコンパイラーが推論します。
この省略記法を使うと、特に要素型が明らかなコレクション操作の呼び出し箇所を短くできます。
struct Person {
let name: String
let age: Int
}
func value(of person: Person, at path: KeyPath<Person, Int>) -> Int {
person[keyPath: path]
}
let frank = Person(name: "Frank", age: 55)
print(value(of: frank, at: \.age))関数の引数としてキーパスを渡す
よくあるパターンは、キーパスを受け取り、それを複数のインスタンスに適用するジェネリック関数です。これにより、何を読み取るかと、読み取りを行うロジックを分離できます。
ここでは、ジェネリックなヘルパーを使って、人のリストから任意の型の任意のプロパティを取り出しています。
struct Person {
let name: String
let age: Int
}
func extract<T>(_ people: [Person], _ path: KeyPath<Person, T>) -> [T] {
people.map { $0[keyPath: path] }
}
let people = [Person(name: "Ana", age: 21), Person(name: "Ben", age: 34)]
print(extract(people, \.name))
print(extract(people, \.age))キーパスと計算プロパティ
読み取り専用のキーパスは、計算プロパティにも対応します。キーパスには計算処理が含まれるため、キーパスを通して読み取るとゲッターが実行されます。
つまり、キーパスは保存プロパティに限定されず、読み取り可能なプロパティであれば対象にできます。
struct Rectangle {
let width: Double
let height: Double
var area: Double { width * height }
}
let areaPath = \Rectangle.area
let r = Rectangle(width: 3, height: 4)
print(r[keyPath: areaPath])自身を表すキーパス
値全体そのものを参照する特殊なキーパスがあります。バックスラッシュ、ドット、self だけで記述し、これを通して読み取るとインスタンスがそのまま返されます。
要素そのものを使いたいのにAPIがキーパスを要求する場合に便利です。たとえば、数値そのものを基準に並べ替える場合などです。
let numbers = [3, 1, 2]
let identity = \Int.self
for n in numbers {
print(n[keyPath: identity])
}キーパスを比較する
キーパスは Hashable と Equatable に準拠しています。同じ型の同じプロパティを指す2つのキーパスは等しいため、集合に保存したり、辞書のキーとして使ったりできます。
これは、プロパティとラベルやフォーマッターを対応付けるテーブルを構築する場合に便利です。
struct Person {
let name: String
let age: Int
}
let labels: [PartialKeyPath<Person>: String] = [
\Person.name: "Full Name",
\Person.age: "Years Old"
]
print(labels[\Person.age] ?? "unknown")理解度チェック:キーパス経由で読み取る
キーパスを通してプロパティを読み取る方法についての理解度を確認しましょう。
まとめ:キーパスの基礎
キーパスの基礎を学びました。
- キーパスは、バックスラッシュ、ルート型、ドット、プロパティ名で記述し、プロパティを読み取らずに表します。
- インスタンスに対して
[keyPath:]サブスクリプトを使うと、値を読み取れます。 - キーパスはネストされたプロパティをたどったり、計算プロパティを対象にしたりできます。
KeyPath、PartialKeyPath、AnyKeyPathは、型安全性と柔軟性のバランスが異なります。- キーパスは値です。保存、受け渡し、比較ができ、辞書のキーとしても使えます。
次はキーパスを使って、コレクション操作をより簡潔に記述する方法を学びます。
struct Person {
let name: String
let age: Int
}
let path = \Person.age
let gina = Person(name: "Gina", age: 48)
print("Read via key path: \(gina[keyPath: path])")よくある質問
「KeyPathの基礎」レッスンは無料ですか?
はい。「KeyPathの基礎」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全4レッスンが含まれています。
「KeyPathの基礎」で何を学びますか?
プロパティを第一級の値として参照します。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Swift Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「KeyPathの基礎」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このSwift Academyレッスンでコードを書いて実行できますか?
はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。