[Swift] KeyPath の理解

なんとなく使っていることが多い KeyPath をきちんと理解してみます。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Monterey 12.1 beta3
  • Xcode 13.2 beta2
  • iOS 15
MEMO
この記事のコードは、Playground で動作します

KeyPath

Apple のドキュメントは、こちら

”A key path from a specific root type to a specific resulting value type” と書かれています。

意訳すると、"指定型の特定プロパティへの key path" でしょうか。

# KeyPath の説明に key path が含まれているのって・・・

簡単な例: KeyPath


// (1)
struct MyStruct {
    let constA: Int
    var varB: Int
}

// (2)
let pathToConstA: KeyPath = \MyStruct.constA

let myStruct = MyStruct(constA: 1, varB: 2)

// (3)
print(myStruct[keyPath: pathToConstA]) // print-out: 1
コード解説
  1. constA と varB というプロパティを持つ struct を定義します
  2. constA というプロパティを指し示す KeyPath を定義します。
  3. KeyPath を使って、struct のプロパティにアクセスします

PartialKeyPath

普段使うコードで、\<struct名>.<プロパティ名> という指定ってあまりしませんよね。\.<プロパティ名> という使い方が多い気がします。

その時に使用されているものが、PartialKeyPath です。Apple のドキュメントは、こちら

ベースとなる型とプロパティ名があれば、プロパティ型は、推測できるので、指定することが不要となります。


struct MyStruct {
    let constA: Int
    var varB: Int
}
struct MyStruct2 {
    let constA: Int
    var varB: Int
}


let myStruct = MyStruct(constA: 1, varB: 2)

// (1)
let pathToConstA: KeyPath<MyStruct, Int> = \MyStruct.constA
print(myStruct[keyPath: pathToConstA])

// (2)
let partialKeyPathToConstA: PartialKeyPath<MyStruct> = \.constA
print(myStruct[keyPath: partialKeyPathToConstA])
コード解説
  1. KeyPath の使用例
  2. PartialKeyPath の使用例

WritableKeyPath

先ほど作成した KeyPath を使用してプロパティを更新しようとするとどうなるかやってみます。


import Foundation

struct MyStruct {
    let constA: Int
    var varB: Int
}

var myStruct = MyStruct(constA: 1, varB: 2)

let pathToVarB: KeyPath>MyStruct, Int> = \MyStruct.varB
myStruct[keyPath: pathToVarB] = 3  // error: cannot assign through subscript: 'pathToVarB' is a read-only key path
print(myStruct[keyPath: pathToVarB])

KeyPath は read-only だとしてコンパイルエラーになります

struct 作成も var 定義に変更していますし、プロパティも var として定義していてもエラーになっています。

このようなケースで必要となるのが、WritableKeyPath です。Apple のドキュメントは、こちら


import Foundation

struct MyStruct {
    let constA: Int
    var varB: Int
}

var myStruct = MyStruct(constA: 1, varB: 2)

let pathToVarB: WritableKeyPath<MyStruct, Int> = \MyStruct.varB
myStruct[keyPath: pathToVarB] = 3
print(myStruct[keyPath: pathToVarB])   // print-out: 3

KeyPath を引数で受ける関数

例えば、KeyPath によって指定されたプロパティの値を表示する関数は以下のようになります。


func printMyStructMember(_ myStruct: MyStruct,_ myKeyPath: PartialKeyPath<MyStruct>) {
    print(myStruct[keyPath: myKeyPath])
}

上記の関数を使用するコードは、以下のようになります。


var myStruct = MyStruct(constA: 1, varB: 2)

printMyStructMember(myStruct, \.constA)

ようやく、見慣れたコード(\.<プロパティ名>)がでてきました。

引数の型が、KeyPath の時には、渡す側で \MyStruct.varB と書かなければいけませんが、もともと対象の struct はMyStruct なので、PartialKeyPath で受け取るのは適切な API 設計と言えると思います。

KeyPath についての考察

KeyPath を使用することで、処理対象のプロパティ名を引数として渡す(指定する)ことができるようになります。

元ネタ?

以下の YouTube をみていて、改めて KeyPath を理解したくて 整理してみました。

まとめ:KeyPath を理解する

KeyPath を理解する
  • 型のプロパティへのアクセス方法の1つ
  • PartialKeyPath を指定すると、型を都度指定しなくとも良くなる
  • プロパティの更新には WritableKeyPath を使用する

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

Swift 学習におすすめの本

詳解Swift

Swift の学習には、詳解 Swift という書籍が、おすすめです。

著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。

最新版を購入するのがおすすめです。

現時点では、上記の Swift 5 に対応した第5版が最新版です。

Swift ポケットリファレンス

Swift を学んでも、プログラミング言語の文法を全て記憶しておくことは無理なので、ちょっとした文法の確認をするために、リファレンス本を手元に置いておくと便利です。

注意
Swift4 までしか対応していないので、相違点を理解して参照する必要があります。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です