1

I have for example codable struct User:

struct User: Codable {
    // ...
    var psnTag: String?
    var xblTag: String?
    var steamTag: String?
    var nintendoTag: String?
    var instagramId: String?
    // ...
}

and stringKey as String = "psnTag"

How can I get the value from instance by stringKey?

Like this:

let stringKey = "psnTag"
user.hasKeyForPath(stringKey) //return Bool
user.valueForPath(stringKey) //return AnyObject
rmaddy
  • 314,917
  • 42
  • 532
  • 579
EvGeniy Ilyin
  • 1,817
  • 1
  • 21
  • 38
  • 1
    A string key path is *objective-c-ish* and error-prone. You (the developer) are supposed to know at **design** time if *user.hasKeyForPath*. That's another pretty expensive unnecessary runtime check. This is Swift. **Take care of types**. Use native Swift `Keypath` e.g. `let keyPath = \User.psnTag` – vadian Dec 22 '18 at 21:35
  • https://stackoverflow.com/a/46597941/2303865 if you would like to implement those method for any Codable struct after adding the extensions in that post `extension Encodable { func hasKey(for path: String) -> Bool { return self[path] != nil } func value(for path: String) -> Any? { return self[path] } }` – Leo Dabus Dec 22 '18 at 21:57

1 Answers1

5

Start with extending Encodable protocol and declare methods for hasKey and for value

Using Mirror

extension Encodable {
    func hasKey(for path: String) -> Bool {
        return Mirror(reflecting: self).children.contains { $0.label == path }
    }
    func value(for path: String) -> Any? {
        return Mirror(reflecting: self).children.first { $0.label == path }?.value
    }
}

Using JSON Serialization

extension Encodable {
    func hasKey(for path: String) -> Bool {
        return dictionary?[path] != nil
    }
    func value(for path: String) -> Any? {
        return dictionary?[path]
    }    
    var dictionary: [String: Any]? {
        return (try? JSONSerialization.jsonObject(with: JSONEncoder().encode(self))) as? [String: Any]
    }
}

Now you can use it like this:

.hasKey(for: "key") //returns Bool
.value(for: "key") //returns Any?
Robert Dresler
  • 10,580
  • 2
  • 22
  • 40
  • you could simply extend Encodable – Leo Dabus Dec 22 '18 at 22:01
  • @LeoDabus btw, once OP wants to use function, I don't think that `subscript` is good way. But again, I can be wrong. – Robert Dresler Dec 22 '18 at 22:13
  • The previous comment had a typo `extension Encodable { subscript(_ key: String) -> Any? { return (Mirror(reflecting: self).children.first { $0.label == key })?.value } }` – Leo Dabus Dec 22 '18 at 22:22
  • 1
    Using `Mirror` in production code just to implement a conversion to dictionary seems a bit too complicated. – Sulthan Dec 22 '18 at 23:53