Published: Oct 17, 2015
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 reflectionWe 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 }
#
Putting it all togetherRather 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 }
Learn about how this is used in practice by reading Enumerate messages of a MIDIPacket in Swift