This is my research notebook. I'm an OSX / iOS indie developer. After 8 years of Objective-C I really enjoy Swift nowadays. Trying to publish all my research on Development, Swift & other technologies here.

Sun, 19 Jul 2015 #

Tuples in Swift, Advanced Usage and Best Practices

This post is also available in 🇨🇳Chinese Thanks to SwiftGG

Tuples are one of Swift's less visible language features. They're occupying a small space between Structs and Arrays. In addition to that, there's no comparable construct in Objective-C (or many other languages). Finally, the usage of tuples in the standard library and in Apple's example code is sparse. One could get the impression that their raison d'être in Swift is pattern machting, but I disgress.

Most tuple explanations concentrate on three tuple use cases (pattern matching, return values, destructuring) and leave it at that. The following guide tries to give a more comprehensive overview of tuples with best practices of when to use them, and when not to use them. I'll also try to list those things that you can't do with tuples, to spare you asking it on stack overflow. Let's dive in.

1 The absolute basics

You'll probably already know most of this, so I'll keep it brief.

Tuples allow you to combine different types into one. Tuples are value types and even though they look like sequences they aren't sequences as there's no direct way of looping over the contents. We'll start with a quick primer on how to create and use tuples.

1.1 Creating and Accessing Tuples

// Constructing a simple tuple
let tp1 = (2, 3)
let tp2 = (2, 3, 4)

// Constructing a named tuple
let tp3 = (x: 5, y: 3)

// Different types
let tp4 = (name: "Carl", age: 78, pets: ["Bonny", "Houdon", "Miki"])

// Accessing tuple elements
let tp5 = (13, 21)
tp5.0 // 13
tp5.1 // 21

let tp6 = (x: 21, y: 33)
tp6.x // 21
tp6.y // 33

1.2 Tuples for pattern matching

As already mentioned above, this feels like the strongest use case for tuples. Swift's switch statement offers a really powerful way of easily defining complex conditionals without cluttering up the source code. You can then match for the type, existence, and value of multiple variables in one statement:

// Contrieved example
// these would be return values from various functions
let age = 23
let job: String? = "Operator"
let payload: AnyObject = NSDictionary()

In the code above, we want to find the persons younger than 30 with a job and a NSDictionary payload. Imagine the payload as something from the Objective-C world, it could be a Dictionary or an Array or a Number. Awful code somebody else wrote years ago and you have to interact with it now.

switch (age, job, payload) {
  case (let age, _?, _ as NSDictionary) where age < 30:
  default: ()

By constructing the switch argument as a tuple (age, job, payload) we can query for the specific or unspecific attributes of all tuple elements at once. This allows for elaborately constrained conditionals.

1.3 Tuples as return types

Probably the next-best tuple use case. Since tuples can be constructed on the fly, they're a great way of easily returning multiple values from one function.

func abc() -> (Int, Int, String) {
    return (3, 5, "Carl")

1.4 Tuple Destructuring

Swift took a lot of inspirations from different programming languages, and this is something that Python has been doing for years. While the previous examples mostly showed how to easily get something into a tuple, destructuring is a swifty way of getting something out of a tuple, and in line with the abc example above, it looks like this:

let (a, b, c) = abc()

Another example is getting several function calls into one line:

let (a, b, c) = (a(), b(), c())

Or, an easy way of swapping two values:

var a = 5
var b = 4
(b, a) = (a, b)

2 Beyond the basics

2.1 Tuples as anonymous structs

Tuples as well as structs both allow you to combine different types into one type:

struct User {
  let name: String
  let age: Int
// vs.
let user = (name: "Carl", age: 40)

As you can see, these two types are similar, yet while the struct is made from a struct description and a struct instance, the tuple exists only as an instance. This similarity can be leveraged whenever you have the need of defining a temporary struct inside a function or method. As the Swift docs say:

Tuples are useful for temporary groups of related values. (…) If your data structure is likely to persist beyond a temporary scope, model it as a class or structure (…)

As an example of this, consider the following situation where the return values from several functions first need to be uniquely collected and then inserted:

func zipForUser(userid: String) -> String { return "12124" }
func streetForUser(userid: String) -> String { return "Charles Street" }

// Find all unique streets in our userbase
var streets: [String: (zip: String, street: String, count: Int)] = [:]
for userid in users {
    let zip = zipForUser(userid)
    let street = streetForUser(userid)
    let key = "\(zip)-\(street)"
    if let (_, _, count) = streets[key] {
	streets[key] = (zip, street, count + 1)
    } else {
	streets[key] = (zip, street, 1)


Here, the tuple is being used as a simple structure for a short interim usecase. Defining a struct would also be possible, but is not strictly necessary.

Another example would be a class which handles algorithmic data, and you're moving an interim result from one method to the next one. Defining an extra struct for something which is only used once in between two or three methods may not be required.

// Made up algorithm
func calculateInterim(values: [Int]) -> (r: Int, alpha: CGFloat, chi: (CGFloat, CGFLoat)) {
func expandInterim(interim: (r: Int, alpha: CGFloat, chi: (CGFloat, CGFLoat))) -> CGFloat {

There's, of course, a fine line here. Defining a struct for one instance is overly complex, defining a tuple 4 times instead of one struct is overly complex too. Finding the sweet spot depends on various factors.

2.2 Private State

In addition to the previous example, there're also use cases where using tuples beyond a temporary scope is useful. Following Rich Hickey's "If a tree falls in the woods, does it make a sound?" as long as the scope is private and the tuple's type isn't littered over the implementation, using tuples for storing internal state can be fine.

A simple and contrieved example would be storing a static UITableView structure which displays various information from a user profile and contains the key path to the actual value as well as a flag whether the value can be edited when tapping on the cell.

let tableViewValues = [(title: "Age", value: "user.age", editable: true),
(title: "Name", value: "", editable: true),
(title: "Username", value: "", editable: false),
(title: "ProfilePicture", value: "", editable: false)]

The alternative would be to define a struct, but if the data is a purely private implementation detail, a tuple works just as well.

A better example is when you defined an object and want to add the ability to add multiple change listeners to your object. Each listener consists out of a name and the closure to be called upon any change:

func addListener(name: String, action: (change: AnyObject?) -> ())
func removeListener(name: String)

How are you storing these listeners in your object? The obvious solution would be to define a struct, but this is a very limited scope, and the struct would only be internal, and it'd only be used in three cases. Here, using a tuple may even be the better solution as the destructuring makes things simpler:

var listeners: [(String, (AnyObject?) -> ())]

func addListener(name: String, action: (change: AnyObject?) -> ()) {
   self.listeners.append((name, action))

func removeListener(name: String) {
    if let idx = listeners.indexOf({ e in return e.0 == name }) {

func execute(change: Int) {
    for (_, listener) in listeners {

As you can see in the execute function, the destructuring abilities make tuples especially useful in this case as the contents are directly destructured into the local scope.

2.3 Tuples as Fixed-Size Sequences

Another area where tuples can be used is when you intend to constrain a type to a fixed amount of items. Imagine an object that calculates various statistics for all months in a year. You need to store a certain Integer value for each month separately. The solution that comes to mind first, would of course be:

var monthValues: [Int]

However, in this case we don't know whether the property indeed contains 12 elements. A user of our object could accidentally insert 13 values, or 11. We can't tell the type checker that this is a fixed size array of 12 items1. With tuples, this specific constraint can easily be put into place:

var monthValues: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)

The alternative would be to have the constraining logic in the object's functionality (say via the new guard statement), however this would be a run time check. The tuple check would be at compile time; your code wouldn't even compile when you try to give 11 months to your object.

2.4 Tuples for Complex Varargs Types

Varargs i.e. variable function arguments are a very useful technique for situations where the amount of parameters of a function is unknown.

// classic example
func sumOf(numbers: Int...) -> Int {
    // add up all numbers with the + operator
    return numbers.reduce(0, combine: +)

sumOf(1, 2, 5, 7, 9) // 24

Tuples can be useful here if your requirement goes beyond simple integers. Take this function which does a batch update of n entities in a database:

func batchUpdate(updates: (String, Int)...) -> Bool {
    for (key, value) in updates {
	self.db.set(key, value)

// We're imagining a weird database
batchUpdate(("tk1", 5), ("tk7", 9), ("tk21", 44), ("tk88", 12))

3 Advanced Tuples

3.1 Tuple Iteration

In the above descriptions, I've tried to stay clear from calling tuples sequences or collections because they aren't. Since every element of a tuple can have a different type, there's no type-safe way of looping or mapping over the contents of a tuple. Well, no beautiful one, that is.

Swift does offer limited reflection capabilities, and these allow us to inspect the contents of a tuple and loop over it. The downside is that the type checker has no way of figuring out what the type within the loop is, and thus everything is typed as Any. It is your job then to cast and match this against your possible types to figure out what to do.

let t = (a: 5, b: "String", c: NSDate())

let mirror = Mirror(reflecting: t)
for (label, value) in mirror.children {
    switch value {
    case is Int:
    case is String:
    case is NSDate:
    default: ()

This is not as simple as array iteration, but it does work if you really need it.

3.2 Tuples and Generics

There's no Tuple type available in Swift. If you wonder why that is, think about it: Every tuple is a totally different type, dependin on the types within it. So instead of defining a generic tuple requirement, you define the specific but generic incarnation of the tuple you intend to use:

func wantsTuple<T1, T2>(tuple: (T1, T2)) -> T1 {
    return tuple.0

wantsTuple(("a", "b")) // "a"
wantsTuple((1, 2)) // 1

You can also use tuples in typealiases, thus allowing subclasses to fill out your types with details. This looks fairly useless and complicated, but I've already had a use case where I need to specifically do this.

class BaseClass<A,B> {
    typealias Element = (A, B)
    func addElement(elm: Element) {
class IntegerClass<B> : BaseClass<Int, B> {
let example = IntegerClass<String>()
example.addElement((5, ""))
// Prints (5, "")

3.3 Define a Specific Tuple Type

In many of the earlier examples, we re-wrote a certain tuple type like (Int, Int, String) multiple times. This, of course, is not necessary per se, as you could define a typealias for it:

typealias Example = (Int, Int, String)
func add(elm: Example) {

However, if you're using a certain tuple construction so often that you think about adding a typealias for it, you might really be better of defining a struct.

3.4 Tuples as function parameters

As Paul Robinson lays out eloquently there's a strange similarity between (a: Int, b: Int, c: String) -> and (a: Int, b: Int, c:String). Indeed, for the Swift compiler, the parameter header of a method / function is nothing more than a tuple:

// Copied from Paul Robinson's blog, you should read the article:

func foo(a: Int, _ b: Int, _ name: String) -> Int     
    return a

let arguments = (4, 3, "hello")
foo(arguments) // returns 4

This looks cool, doesn't it. But wait a minute.. In this case the function signature is a bit specific. What happens when we add or remove labels. Same with the tuple. Well, lets find out:

// Lets try with labels:
func foo2(a a: Int, b: Int, name: String) -> Int {
    return a
let arguments = (4, 3, "hello")
foo2(arguments) // fails to work

let arguments2 = (a: 4, b: 3, name: "hello")
foo2(arguments2) // works! (4)

So labelled tuples are supported if the function signature also has labels.

Do we explicitly need to write the tuple into a variable, though?

foo2((a: 4, b: 3, name: "hello")) // Error

Yeah, bummer, that doesn't work. But what if we get the tuple back from a function call?

func foo(a: Int, _ b: Int, _ name: String) -> Int {
    return a

func get_tuple() -> (Int, Int, String) {
    return (4, 4, "hello")

foo(get_tuple()) // Works! returns 4!

Awesome, this works!

We can also use a typealias to convert one tuple parameter signature into another one:

func foo(a a: Int, b: Int, name: String) -> Int {
    return a
typealias Tp = (a: Int, b: Int, name: String)

// We're leaving out parameter by utilizing a type alias
foo((1, 2, "test") as Tp)

This has a lot of interesting implications and possibilities. When you plan your types well, you can then move parameters around functions without having to destructure the data.

Even better, for functional programming, you can return a tuple with multiple parameters straight into a function without having to destructure it.

Tuples in Swift, Advanced Usage and Best Practices3.5 Tuples to reorder function parameters

Bernd Ohr has a nice example that shows how tuples can be used to reorder function parameters. In his example, he's creating a simple Banana struct, a function createBanana, and a tuple typealias which has the same parameters as the function:

struct Banana { 
    let size: Int 
    let color: Int 
typealias BananaTuple = (size: Int, color: Int)
func createBanana(size size: Int, color: Int) -> Banana {
    return Banana(size: size, color: color)

Now, if you call createBanana you'll get back a Banana. Both, createBanana and the Banana.init initializer require the argument order size, color: I.e. you can't call them like this: createBanana(color: 2, size: 3).

However, if we call the function with a different Tuple, we can cast it to the correct parameter order:

// This will automatically reorder the function parameters
let aBanana = createBanana( (color: 4, size: 1) as BananaTuple)
// prints: : Banana(size: 1, color: 4)

Or, as an array with differently ordered tuples:

let anArray: [BananaTuple] = [(size: 4, color: 1), (color: 2, size: 5)]

4 Tuple impossibilities

Finally, we reach the list of some of the things that are impossible to achieve with tuples.

4.1 Tuples as Dictionary Keys

If you'd like to do the following:

let p: [(Int, Int): String]

Then this is not possible, because tuples don't conform to the hashable protocol. Which is, really, a bummer as the example above has a multitude of use cases. There may be a crazy type checker hack to extend tuples of varying arities to the hashable protocol, but I haven't really looked into that. If you happen to know if this works, feel free to contact me via twitter.

4.2 Tuple Protocol Compliance

Given the following protocol:

protocol PointProtocol {
  var x: Int { get }
  var y: Int { set }

You can't tell the type checker that a tuple (x: 10, y: 20) complies with that protocol.

func addPoint(point: PointProtocol)
addPoint((x: 10, y: 20)) // doesn't work.

5 Addendum

That's it. I probably forgot the one or other thing. Things may also be wrong. If you find a factual error, or if there's something else I forgot, feel free to contact me.

6 Changes

07/23/2015 Added section on tuples as function parameters

08/06/2015 Updated the Reflection example to the latest Swift beta 4. (It removes the reflect call)

08/12/2015 Updated the Tuples as function parameters with a couple more examples and more information.

08/13/2015 Fixed a couple of bugs..

10/28/2015 Fixed bugs and added a new section on parameter reordering.



Interestingly, something which C can do just fine

If you read this far, you should follow me (@terhechte)
on Twitter