Making the Most of Swift.KeyPath

Sam Dods
Kin + Carta Created
3 min readOct 3, 2018
You’re close, but it’s not the one.

DISCLAIMER: This is a proof of concept and by no means am I promoting the approach laid out below. I might offer an opinion at the end. 😇

What I’m playing around with…

Every time I write a map or sorted (etc.) in a Swift project I think: Wouldn’t it be nice if I could simplify this with a key path?

i.e. it would be nice to write people.map { $0.address.city }

as people.map(\.address.city)

I thought about submitting a proposal through the Swift Evolution process but noticed it has already been discussed in great length.

Micro zoom out

⚡️ The KeyPath type was introduced in Swift 4. But I don’t find a lot of use for it in everyday coding, so I started playing around to see if I could make more use of it.

Approach

I’ve taken the approach of defining an operator to convert a key path into a function that can be passed to the standard library methods. The alternative would be to overload those methods.

So instead of implementing my own overloaded version of map etc. I convert the key path KeyPath<A, B> into the necessary function type (A) -> B.

The benefit of this approach is that I don’t need to overload all the variations of each method, i.e. I don’t need to overload map, compactMap and flatMap. I just implement one operator for all methods with the same signature.

I created a Swift Playground to try it out and thought it would be worth sharing, so here we go…

First I need a data model to play with…

What mapping looks like…

The new syntax for sorting…

For the sake of my proof of concept, I sort such that elements at the start of the list are “less than” elements at the end of the list, i.e. I sort using the provided key path and the < operator. If the reverse was required, you could do the same and add .reversed() to the end.

Filtering using Boolean properties…

Filtering is supported for properties (key paths) that return a Bool. So for example, we could filter all strings where isEmpty is true, or we could filter all controls where isEnabled is true, or all views where isHidden is true.

A load of other methods have the same signature as filter, e.g. first(where:):

This can be further enhanced with a simple extension on Bool, so as well as filtering all empty strings, we can filter all non-empty strings:

Conclusion

It looks like this won’t be added to the standard library any time soon and I don’t think that’s a bad thing.

On the surface this syntactic sugar seems nice and it’s fun to play around with. But on reviewing a very large codebase I’m working on, I only found 54 occurrences where I’m using the syntax map { $0.some.key.path }. And is it really that ugly?

Overloading the methods is less elegant because it requires all individual methods to be overridden. But implementing an operator to convert a key path to a function has its disadvantages too. There may be an example of a function with the same signature as, say, map, but to which it wouldn’t make sense to pass a key path.

Oh and of course custom operators themselves are controversial (but I’m not here to offer an opinion on that 😉).

Bottom line is I’ll keep this as a proof of concept for now :)

Hope you enjoyed this article, follow me on Twitter for more of the same, or check out the other articles on the TAB blog.

Full Gist is here. Swift Playground is here.

P.S. If you’re excited by advancements in Swift and iOS and you want to be part of our passionate team, head on over to our hiring page.

--

--

Sam Dods
Kin + Carta Created

Tech Lead and Mobile Evangelist based in Edinburgh, Scotland