The story of my Experiments with Swift KeyPath

Each key 🗝 is destined for single path

Aaina jain
4 min readJun 13, 2020
Kudos by DarkCube Studio for the great illustration!
Credit: Unsplsh.com

KeyPath refers to a property itself instead of property’s value. They‘re useful when you want to perform operations on property, rather on the value behind it.

Let’s take an example:

KeyPath let us refer to the properties without accessing them using . like this: \type name.path .

nameKeyPath syntax in the screenshot is <User, String> where User is type name and String is path name.

This struct has an issue now, it won’t let me update nameKeyPath value as it’s declared let. It gives below error on assigning value so to update nameKeyPath value from outside of type need to declare it public.

Let’s make the name of User public, on making it public key path becomes WritableKeyPath<User, String>. Values can be assigned only on WritableKeyPath

Although it helps in meeting criteria but the type is mutated from outside, had to compromise by making name and user property var.

Let’s use private(set)

Now I have 2 problems:

  • I don’t have access to WritableKeyPath outside Type, so compiler gives error on assigning data using KeyPath
  • Another is I are mutating property, ‘mutating’ function can only be called on vars

To mitigate this problem, I created a protocol that can be called on any struct to make a new copy with only one property changed.

Modify Struct Property value using KeyPath

Using update function I can update value of any property.

First KeyPath is casted toWritableKeyPath and then update(keyPath:value:) is called on it.

This solution works perfectly for updating any property value but let’s say I have to update the value of 3 properties that requires us to repeat the code of casting multiple times. So let’s move casting to protocol extension. While moving casting code I realized that instead of KeyPath I should pass PartialKeyPath which just accepts root type.

  • PartialKeyPath — It’s useful when you need to send keyPath to another function or you want to store keypaths of similar type.
  • AnyKeyPath — It’s useful when you want to store key paths of any type into an array.

Finally 🎉 KeyPathEditable protocol can be conformed by any struct to make a new copy with only one property changed

Complete source code can be found here

Modify class property using KeyPath

We can’t use existing KeyPathEditable protocol here as that creates a copy on modifying property value. Let’s create a new protocol to modify a property value only

If you notice apart from copy there is one more difference b/w this protocol and previous protocol. With class type we can’t specify Self type in protocol, it gives error:

Error: protocol can only be used as a generic constraint because it has Self or associated type requirements

To fix this error I used associatedtype Root and passed type in function argument

Protocol can bee consumed like this:

Finally 🎉 ReferenceKeyPathEditable protocol can be conformed by any class to modify property value while keeping property setter private for other types.

Complete source code can be found here

KeyPath with Functions:

  • map()

KeyPath with functions can be used when you have a requirement to work with a single property. I mostly like this for high level functions.

It helps while reading as well. In a map with closure, our intention is to map user by name property but we specify by value. Although it works but keyPath fits better here as by keyPath we say to map by given path.

  • filter() — keypath can be used with a filter as well
  • sort()

Swift standard library doesn’t provide a function to sort by keypath but we can add utility function in extension:

Complete source code can be find here

P.S: It was fun playing with it

For more insights, this article is worth to read

https://klundberg.com/blog/swift-4-keypaths-and-you/

You can reach out to me at:

Linkedin: Aaina Jain

Twitter: __aainajain

--

--