featherless
software design

Enumerating tuple values in Swift

This post is also available as a Swift playground at https://github.com/jverkoey/playgrounds.

Consider the following:

let fibonacci = (0, 1, 1, 2, 3, 5, 8, 13, 21, 34)  

How might we iterate over the tuple's values? We might try a for-in loop:

for val in fibonacci {  
  val
}

But tuples don't conform to SequenceType which the resulting error reiterates:

Type '(Int, Int, ..., Int)' does not conform to protocol 'SequenceType'

So we might then try to enumerate using a subscript:

fibonacci[0]  

But tuples don't implement subscript so we're met with the following error:

Type '(Int, Int, ..., Int)' has no subscript members

So how do we iterate over our tuple? The answer is provided by Swift's Mirror type. From the docs:

[Mirror is] a representation of the sub-structure and optional "display style" of any arbitrary subject instance.

Describes the parts---such as stored properties, collection elements, tuple elements, or the active enumeration case---that make up a particular instance.

Tuple reflection

We create a Mirror by initializing it with the object we intend to reflect.

let mirror = Mirror(reflecting: fibonacci)  

The resulting mirror object allows us to enumerate through the values of the object's children.

for child in mirror.children {  
  child.value
}

Swift Playground output

Putting it all together

Rather than create a Mirror every time we want to enumerate a tuple, let's build a helper function that turns tuples into enumerable types.

func generatorForTuple(tuple: Any) -> AnyGenerator<Any> {  
  return anyGenerator(Mirror(reflecting: tuple).children.lazy.map { $0.value }.generate())
}
  • We take advantage of Swift's lazy map in order to lazily extract the value from each child.
  • The return value is wrapped in an anyGenerator in order to erase the LazyMapGenerator implementation detail.

We can now iterate over tuples like so:

for val in generatorForTuple(fibonacci) {  
  val
}