Trying to achieve this might sound silly, as the goal of keypath is to eliminate string and be strict. But I do need it in a particular problem, where I need to keep a list of properties that has special behaviours.

struct Book {
    var title: String
    var rating: Int

// The goal is to get & set using string for the properties
var book = Book(title: "", rating: 0)
book.set("title", "Lord of the Bing")

Return keypath from a string

To return PartialKeyPath given a string, we need to exhaustively list for each of the property. While this is tedious, it is possible to make use of Swift Macros to generate such repetitive code.

extension Book {
    static func keyPath(from s: String) -> PartialKeyPath<Self>? {
        switch s {
        case "title": return \Self.title
        case "rating": return \Self.rating
        default: return nil


func `get`(_ s: String) -> Any? {
    guard let kp = Self.keyPath(from: s) else { return nil }
    return self[keyPath: kp]


Setting requires duplicating the code for all the property types, as it is necessary when casting to WritableKeyPath.

mutating func `set`(_ s: String, _ x: Any) {
    guard let kp = Self.keyPath(from: s) else { return }
    switch kp {
    case let w as WritableKeyPath<Self, String>: if let x = x as? String { self[keyPath: w] = x }
    case let w as WritableKeyPath<Self, Int>: if let x = x as? Int { self[keyPath: w] = x }
    default: break

Alternative: Use reflection

I found an implementation for KeyPathListable, which uses Mirror to provide allKeyPaths – a dictionary of property string to keypaths.

protocol KeyPathListable {
    var allKeyPaths: [String: PartialKeyPath<Self>] { get }

extension KeyPathListable {
    private subscript(checkedMirrorDescendant key: String) -> Any {
        return Mirror(reflecting: self).descendant(key)!

    var allKeyPaths: [String: PartialKeyPath<Self>] {
        var membersTokeyPaths = [String: PartialKeyPath<Self>]()
        let mirror = Mirror(reflecting: self)
        for case (let key?, _) in mirror.children {
            membersTokeyPaths[key] = \Self.[checkedMirrorDescendant: key] as PartialKeyPath
        return membersTokeyPaths

extension Book: KeyPathListable {}

It is a handy protocol extension.

However, you can’t set using mirror..




Back to Home