Let's say we want to dig a value out of pile of JSON. For example, what if we want to pull Bobbin's name out of the following:

{ "characters":{ "LucasArts":[ {"name":"Guybrush Threepwood"}, {"name":"Bobbin Threadbare"}, {"name":"Manny Calavera"} ] } }

We might represent the path1 used to get to this data with an array like so:

["characters", "LucasArts", 1, "name"]

The problem is, JSON freely mixes dictionaries with arrays. So some of our path elements are String s, while some are Int indexes. Swift arrays are homogenous — that is, everything in them has to have the same type. We can't mix-and-match String s and Int s in a type-safe Array .

We've talked before about how Swift enumerations are sum types that can represent one of a number of different types. So we can create an enum that represents both String -based keys and Int -based indexes, and use it as the type of our array:

enum JSONSubscript{ case Key(String), Index(Int) } let path:[JSONSubscript] = [.Key("characters"), .Key("LucasArts"), .Index(1), .Key("name")]

Which works and has a nice sort of DSL feel to it. But if we use more than a handful of enumerations in our code, it's dangerously easy for us to be overwhelmed by a flood of .This("thing") and .That("thing") . Can we do better?

An oft-overlooked feature of Swift enumerations is that we can define initializers for them, just like class es and struct s:

enum JSONSubscript{ case Key(String), Index(Int) init(value:String){ self = .Key(value) } init(value:Int){ self = .Index(value) } }

And if we can write initializers, we can conform to ...LiteralConvertible protocols.

Mattt Thompson has written a fantastic explanation of literal convertibles that we should all read. But to summarize, a type that conforms to one of the literal convertible protocols can use the given literal to initialize itself.

To put it another way, we usually think of 5 as being a literal representation of an Int . But if JSONSubscript were to implement the IntegerLiteralConvertible protocol, 5 could also be a literal representation of JSONSubscript .

All it takes is a few init s… and some typealias es in the case of StringLiteralConvertible :

enum JSONSubscript : IntegerLiteralConvertible, StringLiteralConvertible{ case Key(String), Index(Int) //for IntegerLiteralConvertible: init(integerLiteral value: IntegerLiteralType){ self = .Index(value) } //for StringLiteralConvertible init(unicodeScalarLiteral value:String){ self = .Key(value) } init(extendedGraphemeClusterLiteral val:String){ self = .Key(val) } init(stringLiteral value:StringLiteralType){ self = .Key(value) } }

Now, I'll be the first to concede the implementation of StringLiteralConvertible is pretty verbose2…

[UPDATE: thanks to a little help from the message boards, the above is now much less long-winded. I hereby retract my claims of verbosity!]

…But in exchange, look what our path array has become:

let path:[JSONSubscript] = ["characters", "LucasArts", 1, "name"]

Simple, beautiful data without annotation or distraction.

By "path" here I'm talking about something like JSONPath — or XPath in the world of XML.↩︎

They're aware of it.↩︎