Introduction to Swift Keypaths

Type-Casting KeyPaths

Type-Casting KeyPaths

As we've seen before, Swift offers PartialKeyPath<Root> and AnyKeyPath as a way to generalize KeyPath handling. While this makes it easier to store these keypaths in generic functions and arrays, it makes it much harder to actually use them. There's not much we can do with them - except for printing maybe - which is why most of our usage examples were about printing.

Type-casting changes this. It allows you to cast a type-erased KeyPath back into (for example) a WritableKeyPath in a totally type-safe manner. Observe the magic:

let keyPath: AnyKeyPath = \User.username
var user = User(username: "Hello")

if let writableUsername = keyPath as? WritableKeyPath<User, String> {
   user[keyPath: writableUsername] = "World"
}

In this example, you saw how we converted an AnyKeyPath back into a WritableKeyPath<User, String>. The important part is that as? returns on optional. So if the KeyPath is not of type <User, String> nothing would happen as the expression would return nil. Therefore, in order to work with multiple types (String, Int, Float, etc), you need more than one if statement. Preferrably a switch statement.

In this example, we're using switch to identify the specific type of the keyPath and then perform a different operation depending on the type.

let keyPath: AnyKeyPath = \User.username
switch keyPath {
case let a as KeyPath<Yeah, String>:
    print("String" + xxx[keyPath: a])
case let a as KeyPath<Yeah, Int>:
    print(1 + xxx[keyPath: a])
default:
    print("Unknown keypath type")
}