Search code examples
swiftswift-dictionary

How to write Dictionary extension that handles optional values


I'm trying to implement a Dictionary extension and I want to handle optional values. But whatever I do, if I use my method on a [String: String?] dictionary, it fails to optionally bind the value. How do you write an extension to a dictionary that gracefully handles optional values?


Consider the following extension:

extension Dictionary {
    func someMethod() {
        for (key, value) in self {
            if let valueString = value as? String {
                println("  \(key) = \(valueString)")
            } else {
                println("  \(key) = \(value) cannot be cast to `String`")
            }
        }
    }
}

So consider the following code:

let dictionary: [String: AnyObject?] = ["foo": "bar"]
dictionary.someMethod()

And it curiously reports

foo = Optional(bar) cannot be cast to `String`

I can write a non-extension method that handles dictionary parameters with optional values, but don't see how to do it as an extension of Dictionary.


Solution

  • You could do this with reflection. Doesn't require much more code than you already have:

    extension Dictionary
    {
        func someMethod()
        {
            for (key, value) in self
            {
                var valueRef = _reflect(value)
    
                while valueRef.disposition == .Optional && valueRef.count > 0 && valueRef[0].0 == "Some"
                {
                    valueRef = valueRef[0].1
                }
    
                if let valueString: String = valueRef.value as? String
                {
                    print("  \(key) = \(valueString)")
                }
                else
                {
                    print("  \(key) = \(value) cannot be cast to `String`")
                }
            }
        }
    }
    

    let dictionary: [String : AnyObject?] = ["foo" : "bar"]
    dictionary.someMethod()
    

    Returns

    foo = bar
    

    let dictionary: [String : AnyObject?] = ["foo" : nil]
    dictionary.someMethod()
    

    Returns

    foo = nil cannot be cast to `String`
    

    let dictionary: [String : AnyObject?] = ["foo" : UIViewController()]
    dictionary.someMethod()
    

    Returns

    foo = Optional(<UIViewController: 0x7fee7e819870>) cannot be cast to `String`