SwiftUIを使っていると出てくる\.記法について調べてみました。
この記法は以下のようにForEachなどで使われます。
struct ContentView: View { var body: some View { let models = [ MyStruct(id: 1, name: "name1"), MyStruct(id: 2, name: "name2"), ] return ForEach(models, id: \.id) { Text($0.name) } } } struct MyStruct { let id: Int let name: String }
ForEachのinitの引数の型を見ると下のようなKeyPath型になっています。
public init(_ data: Data, id: KeyPath<Data.Element, ID>, content: @escaping (Data.Element) -> Content)
KeyPathはSwift4で追加された機能で、以下のような形でプロパティーの値を取得できるようになります。
let keyPath = \MyStruct.name let myStruct = MyStruct(id: 1, name: "name1") myStruct[keyPath: keyPath] // → "name1"、myStruct.nameと同等の結果
KeyPathは下のようにクラスを省略することができます。
let keyPath: KeyPath<MyStruct, String> = \.name // let keyPath = \MyStruct.nameと同等
つまりForEach一番最初に出てきたForEachのバックスラッシュは、KeyPathを渡しているだけでした。
分かりやすく書くと下の書き方と同等になります。
struct ContentView: View { var body: some View { let models = [ MyStruct(id: 1, name: "name1"), MyStruct(id: 2, name: "name2"), ] let keyPath: KeyPath<MyStruct, Int> = \MyStruct.id return ForEach(models, id: keyPath) { Text($0.name) } } }
余談ですがKeyPathはSwift5.2でメソッドに変換できるようにもなりました。
これを使うとmapなどは下のように書くことができるようになります。
let models = [ MyStruct(id: 1, name: "name1"), MyStruct(id: 2, name: "name2"), ] models.map(\.id) // → [1, 2]、models.map { $0.id }と同じ結果
内部的には以下のように変換されているようです。
models.map(\.id) // ↓ のように変換 models.map { $0[keyPath: \.id] }
下のように独自のメソッドでも使うことができます。
myMethod(closure: \.id) func myMethod(closure: (MyStruct) -> (Int)) { print(closure(MyStruct(id: 3, name: "name3"))) // → 3 }