Are KeyPaths Codable?

I've a pretty simple question: Can a KeyPath be encoded and decoded?

Our team decided to use KeyPaths partly because of a weakness of pointers, which is that a pointer is only valid addressing memory on the machine it was made on. This makes it less useful for our application, which is a distributed system on more that one computer. It seemed to us at the time that a KeyPath, since it is instance-agnostic, should be able to work on the machine it was made on as well as any other machine.
However, as I've tried to conform KeyPath to Codable, I've run into compiler errors every way I've approached it. I'm second guessing whether they are Codable, since I don't understand how KeyPaths are implemented.
As an ancillary question, is there a simple way to explain what a KeyPath actually is? Like how is it implemented?

KeyPaths are not currently Codable. This would be an interesting future direction, but if done naively, it could be a security issue, since dereferencing an arbitrary key path could lead to arbitrary code execution. Part of the eventual design for coding key paths would be to have some way of marking types, properties and other declarations as coding-safe, similar to Foundation's NSSecureCoding concept.

If you're interested in what a key path object looks like under the hood, this document in the swift compiler is still mostly accurate:

6 Likes

Perfect, thanks Joe. I'd been wondering about KeyPaths under the hood, but couldn't make any progress reading the KeyPaths.swift source. Glad you pointed that document out.

We've been using KeyPaths inside of a Lens object to give access to data in a uniform way. A BoundLens packages the data itself and the KeyPaths to mutate it. It's nice for an actor to have a way to mutate data without caring what it actually is. Great for some of our ML applications.

We've been hesitant to adopt Foundation in the past because our application will run on a cluster of Linux machines, and we aren't sure about access to all the extra Foundation functionality you can get on OSX compared to Linux. Do you see a workaround to be able to encode and decode KeyPaths through some Foundation functionality like NSSecureCoding, or anything else? Any pointers would be appreciated.

Well, if you know the set of keys intend to allow to be serialized and deserialized up front, you can write the mapping yourself:

func keyString(for key: AnyKeyPath) -> String? {
  switch key {
  case \MyObject.foo: return "foo"
  case \MyObject.bar: return "bar"
  default: return nil
  }
}

func key(from string: String) -> AnyKeyPath? {
  switch string {
  case "foo": return \MyObject.foo
  case "bar": return \MyObject.bar
  default: return nil
  }
}

You might be able to use a code generator like Sourcery to automate the generation of these functions for you.

1 Like

Ok, this makes perfect sense for serializing KeyPaths with a "length" of 1 (a Root type and a Value type with no types or indices in between).

But if you wanted to use your mapping for Keypaths of longer "lengths", wouldn't there be a combinatorial explosion of cases to generate?

for example, in order to have a key like this:
\Tree.branches[3].leaves[7])

you would have to define
case "Tree.branches[0].leaves[0]": return \Tree.branches[0].leaves[0]
case "Tree.branches[0].leaves[1]": return \Tree.branches[0].leaves[1]
case "Tree.branches[0].leaves[2]": return \Tree.branches[0].leaves[2]
...maybe thousands more...

Is there another way to do this manual mapping for longer KeyPaths?
Thanks

Not with public API, unfortunately. There would need to be a way to iterate through the components of a key path.

1 Like
Terms of Service

Privacy Policy

Cookie Policy