しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

SwiftUIで出てくるバックスラッシュの意味を調べる

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
}

参考URL

Swift 5.2の新機能 - Qiita