14

In Swift 4 many on the Foundation team have discussed how much easier it is to use keyPaths as compared to Swift 3. This begs the question... What is a keyPath? Seriously, I can't find any clear resources.

Brandon Bradley
  • 2,720
  • 4
  • 19
  • 38
  • 2
    https://developer.apple.com/documentation/swift/key_path_expressions; https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/BasicPrinciples.html#//apple_ref/doc/uid/20002170-BAJEAIEE – jscs Jun 09 '17 at 02:10
  • 1
    I liked this blogpost: https://www.klundberg.com/blog/swift-4-keypaths-and-you/ – pushkarnk Nov 14 '17 at 13:29
  • 1
    https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md – pushkarnk Nov 14 '17 at 14:31

2 Answers2

16

Objective-C has the ability to reference a property dynamically rather than directly. These references, called keypaths. They are distinct from direct property accesses because they don't actually read or write the value, they just stash it away for use.

Let define a struct called Cavaliers and a struct called Player, then create one instance of each:

// an example struct
struct Player {
    var name: String
    var rank: String
}

// another example struct, this time with a method
struct Cavaliers {
    var name: String
    var maxPoint: Double
    var captain: Player

    func goTomaxPoint() {
        print("\(name) is now travelling at warp \(maxPoint)")
    }
}

// create instances of those two structs
let james = Player(name: "Lebron", rank: "Captain")
let irving = Cavaliers(name: "Kyrie", maxPoint: 9.975, captain: james)

// grab a reference to the `goTomaxPoint()` method
let score = irving.goTomaxPoint

// call that reference
score()

The last lines create a reference to the goTomaxPoint() method called score. The problem is, we can't create a reference to the captain's name property but keypath can do.

let nameKeyPath = \Cavaliers.name
let maxPointKeyPath = \Cavaliers.maxPoint
let captainName = \Cavaliers.captain.name
let cavaliersName = irving[keyPath: nameKeyPath]
let cavaliersMaxPoint = irving[keyPath: maxPointKeyPath]
let cavaliersNameCaptain = irving[keyPath: captainName]

Please test with Xcode 9 or capable snapshot.

pkamb
  • 30,595
  • 22
  • 143
  • 179
Durul Dalkanat
  • 6,980
  • 4
  • 33
  • 36
0

Swift KVC

[Objective-C KVC]

KeyPath is a reference to a property of a type rather than value. It adds a dynamism into language

There are some of them:

  • KeyPath<Root, Value> - only read
  • WritableKeyPath<Root, Value> - read/write for var property
  • ReferenceWritableKeyPath<Root, Value> - read/write only for reference types[About]
  • PartialKeyPath<Root>
  • AnyKeyPath

You can find that KeyPath is used for shortcuts(e.g. sorting, iterating, filtering), KVO[Example], MemoryLayout[Example], SwiftUI and others more advanced features

Syntax

class SomeClass {
    var v: String = "Hello World"
}

Pre Swift v4 - slow and not type safe

@objc //For example var v: String = "Hello world"
let keyPath = #keyPath(SomeClass.v) //keyPath is String Type
//read
someClass.value(forKeyPath: keyPath) //or forKey:
//write
someClass.setValue("Another string", forKeyPath: keyPath) //or forKey:
  • Starts from backwards slash \
let someKeyPath = \SomeClass.v //KeyPath<SomeClass, String>
  • If there is a known object you can use \.
someClass.observe(\.v, options: .new) //someClass1.v
//read
let res = someClass[keyPath: \SomeClass.v]

//write
someClass[keyPath: \.v] = "Another string"
yoAlex5
  • 21,739
  • 5
  • 148
  • 151