Subscript: access my dictionary values with a String enumeration

↘锁芯ラ 提交于 2019-11-30 17:03:55

The simplest approach is to just lift the dictionary into more context. The context in this case is "it only has keys from this enum." Lifting a type in Swift is very straightforward. Just wrap it in a struct.

// This could be a nested type inside JSONObject if you wanted.
enum JSONKeys: String {
    case district
}

// Here's my JSONObject. It's much more type-safe than the dictionary,
// and it's trivial to add methods to it.
struct JSONObject {
    let json: [String: AnyObject]
    init(_ json: [String: AnyObject]) {
        self.json = json
    }

    // You of course could make this generic if you wanted so that it
    // didn't have to be exactly JSONKeys. And of course you could add
    // a setter.
    subscript(key: JSONKeys) -> AnyObject? {
        return json[key.rawValue]
    }
}

let address: [String: AnyObject] = ["district": "Bob"]

// Now it's easy to lift our dictionary into a "JSONObject"
let json = JSONObject(address)

// And you don't even need to include the type. Just the key.
let district = json[.district]

Try this:

extension Dictionary where Key: StringLiteralConvertible {
    subscript(index: JsonKeys) -> Value {
        get {
            return self[index.rawValue as! Key]!
        }
    }
}

Remember, with having constraint as Key: StringLiteralConvertible, the extension works for any Dictionaries with its Key conforming to StringLiteralConvertible. (You know many types other than String conform to StringLiteralConvertible.)

To call subscript self[], you need to pass a value of type Key. index.rawValue is String, which may not always be a Key in the extension.

So, the extension I have shown would work for some Dictionaries, would cause runtime crash for some other Dictionaries.


A little bit more type-safe way:

protocol MyJsonKeysConvertible {
    init(jsonKeys: JsonKeys)
}
extension String: MyJsonKeysConvertible {
    init(jsonKeys: JsonKeys) {self = jsonKeys.rawValue}
}
extension Dictionary where Key: MyJsonKeysConvertible {
    subscript(index: JsonKeys) -> Value {
        get {
            return self[Key(jsonKeys: index)]!
        }
    }
}

I know that this is an old question, but I'd thought I'd add an implementation that is easier to extend, reuse, and more lightweight

public protocol UsesRawValue {
    var rawValue: String { get }
}

extension JsonKeys: UsesRawValue {}

extension Dictionary where Key: ExpressibleByStringLiteral {
    public subscript(key: UsesRawValue) -> Value? {
        get { return self[key.rawValue as! Key] }
        set { self[key.rawValue as! Key] = newValue }
    }
}

Based on this blog post

This approach only requires us to extend our dictionary once, rather than for each enum. Instead, each enum needs to conform to UsesRawValue. Now we can use it like this.

ajson[JsonKeys.key1]
ajson[JsonKeys.key1] = "name"
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!