lateral leandrinux
lateral leandrinux

Reputation: 19

What's an elegant way to define an immutable array of dictionaries in Swift 2?

I'd like to define a static array of dictionaries using the cleanest, most readable syntax that is possible with Swift 2. Unfortunately I keep getting Type of expression is ambiguous without more context errors.

For instance:

    let items: [[MyEnum:AnyObject]] = [
        [ .EnumKey: 1, .AnotherKey: ["cat", "dog", "bird"] ],
        [ .EnumKey: 2, .AnotherKey: ["racoon"] ]
    ]

I did google for an answer but couldn't find a good example. Some recommend using var and adding one value per line, but I feel that's messy because I know that the array and its items won't change. Any help is appreciated!

Upvotes: 1

Views: 92

Answers (1)

Mr Beardsley
Mr Beardsley

Reputation: 3863

The differences you are seeing result from whether you import Foundation or not.

In your example, you type your dictionaries as:

[MyEnum:AnyObject]

However, you initialize them with Ints and Arrays. In a strict Swift world, Int and Array are structs and don't conform to AnyObject. AnyObject is a protocol to which only classes implicitly conform. However, once you import Foundation then things get bridged to NSNumber, NSString, NSArray, etc. These are all classes and evidently this creates an ambiguity for the compiler. The error message is correct, but somewhat hard to understand until you know what is happening behind the scenes.

I would suggest changing AnyObject to Any.

enum MyEnum {
     case EnumKey
     case AnotherKey
}

let items: [[MyEnum:Any]] = [
        [ .EnumKey: 1, .AnotherKey: ["cat", "dog", "bird"] ],
        [ .EnumKey: 2, .AnotherKey: ["racoon"] ]
    ]

This should work and is technically more correct.

However, if you need your dictionaries to bridge with NSDictionary then you will have to go another way. In that instance you can use AnyObject, but you will have to type your values as NSNumber and NSArray. Also, I changed your enum to have String raw values to use as the keys.

enum MyEnum: String {
    case EnumKey
    case AnotherKey
}

let items: [[String:AnyObject]] = [
        [ MyEnum.EnumKey.rawValue: NSNumber(integer: 1), MyEnum.AnotherKey.rawValue: ["cat", "dog", "bird"] as NSArray ],
        [ MyEnum.EnumKey.rawValue: NSNumber(integer: 2), MyEnum.AnotherKey.rawValue: ["racoon"] as NSArray ]
    ]

For more information on the weird behavior of AnyObject, this blog had some good information: http://www.drewag.me/posts/swift-s-weird-handling-of-basic-value-types-and-anyobject

Upvotes: 2

Related Questions