LoginSignup
2
2

More than 1 year has passed since last update.

【Swift】知っておきたいKeyPathの基本と使い方

Last updated at Posted at 2022-01-24

この投稿は何?

SwiftにおけるKeyPathについて、基本から解説します。

実行環境

  • macOS 12.1
  • Xcode 13.2.1
  • Swift 5.5

KeyPathとは

要は、「あるデータ型に定義されたプロパティまでの参照(パス)」です。

Swift3時代のString KeyPath

KeyPathは、Objective-Cの時代から利用されていました。
Swiftでもそれを受け継いでおり、クラスに`objcMembers'属性をマークすることで利用できました。

参照型データの場合
@objcMembers class Kid: NSObject {
    dynamic var nickname: String = ""
    dynamic var age: Double = 0.0
    dynamic var bestFriend: Kid? = nil
    dynamic var friends: [Kid] = []

    init(nickname: String, age: Double) {
        self.nickname = nickname
        self.age = age
    }
}

// ベン君
let ben = Kid(nickname: "Benji", age: 5.5)

// Kid型のnicknameプロパティへのキーパスを作成
let kidsNameKeyPath = #keyPath(Kid.nickname)    // String

// キーパスを使って取得した「ベン君のニックネーム」はAny型になってしまう
let name = ben.value(forKeyPath: kidsNameKeyPath)   // value(forKeyPath: String) -> Any
print(name as! String)    // Benji

この方法で取得したキーパスは、単純なString型として保持されます。
そして、String KeyPathを利用して取得したプロパティ値は、型情報が失われてしまう性質がありました。

Swiftに最適化されたKeyPath

Swift4では、全く新しい方法でKeyPathを利用できるようになりました。

値型データの場合
struct BirthdayParty {
    let celebrant: Kid
    var theme: String
    var attending: [Kid]
}

var bensParty = BirthdayParty(celebrant: ben, theme: "Construction", attending: [])
let birthdayKid = bensParty[keyPath: \BirthdayParty.celebrant]
bensParty[keyPath: \BirthdayParty.theme] = "Ninja"

let birthdayKidsAgeKeyPath = \BirthdayParty.celebrant.age           // キーパスを作成
let birthdayboysAge = bensParty[keyPath: birthdayKidsAgeKeyPath]    // 5.5

値型データのKeyPathでは型情報が維持され、よりシンプルに記述されます。
Property.png

基点型が推論可能な場合、記述を省略できます。
age = ben[keyPath Kid.age].png

benKid型インスタンスなので、基点型はKidであることが推論可能です。
従って、Kidを省略して以下のように記述できます。

let age = ben[KeyPath: \.age]

同様に、bensPartyBirthdayParty型インスタンスなので、以下のコードも略記できます。

bensParty[keyPath: \BirthdayParty.theme] = "Ninja"
bensParty[keyPath: \.theme] = "Ninja"    // 型推論による基点型の省略記法

この方法で取得したキーパスは、ReferenceWritableKeyPath型として保持されます。
そして、取得した値の型情報も維持されます。
スクリーンショット 2022-01-23 15.55.15.png

クラスのキーパス

クラスでも同じようにして、キーパスを利用できます。

class C {
    var p = ""
}
let c = C()

let cpKeyPaty = \C.p        // ReferenceWritableKeyPath<C, p>
let cp = c[keyPath: \C.p]   // String
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2