NEW: Learn to build the incredible iOS 15 Weather app today! >>

How to return multiple values from functions

Paul Hudson    @twostraws   

Updated for Xcode 13.2

When you want to return a single value from a function, you write an arrow and a data type before your function’s opening brace, like this:

func isUppercase(string: String) -> Bool {
    string == string.uppercased()
}

That compares a string against the uppercased version of itself. If the string was already fully uppercased then nothing will have changed and the two strings will be identical, otherwise they will be different and == will send back false.

If you want to return two or more values from a function, you could use an array. For example, here’s one that sends back a user’s details:

func getUser() -> [String] {
    ["Taylor", "Swift"]
}

let user = getUser()
print("Name: \(user[0]) \(user[1])") 

That’s problematic, because it’s hard to remember what user[0] and user[1] are, and if we ever adjust the data in that array then user[0] and user[1] could end up being something else or perhaps not existing at all.

We could use a dictionary instead, but that has its own problems:

func getUser() -> [String: String] {
    [
        "firstName": "Taylor",
        "lastName": "Swift"
    ]
}

let user = getUser()
print("Name: \(user["firstName", default: "Anonymous"]) \(user["lastName", default: "Anonymous"])") 

Yes, we’ve now given meaningful names to the various parts of our user data, but look at that call to print() – even though we know both firstName and lastName will exist, we still need to provide default values just in case things aren’t what we expect.

Both of these solutions are pretty bad, but Swift has a solution in the form of tuples. Like arrays, dictionaries, and sets, tuples let us put multiple pieces of data into a single variable, but unlike those other options tuples have a fixed size and can have a variety of data types.

Here’s how our function looks when it returns a tuple:

func getUser() -> (firstName: String, lastName: String) {
    (firstName: "Taylor", lastName: "Swift")
}

let user = getUser()
print("Name: \(user.firstName) \(user.lastName)")

Let’s break that down…

  1. Our return type is now (firstName: String, lastName: String), which is a tuple containing two strings.
  2. Each string in our tuple has a name. These aren’t in quotes: they are specific names for each item in our tuple, as opposed to the kinds of arbitrary keys we had in dictionaries.
  3. Inside the function we send back a tuple containing all the elements we promised, attached to the names: firstName is being set to “Taylor”, etc.
  4. When we call getUser(), we can read the tuple’s values using the key names: firstName, lastName, etc.

I know tuples seem awfully similar to dictionaries, but they are different:

  1. When you access values in a dictionary, Swift can’t know ahead of time whether they are present or not. Yes, we knew that user["firstName"] was going to be there, but Swift can’t be sure and so we need to provide a default value.
  2. When you access values in a tuple, Swift does know ahead of time that it is available because the tuple says it will be available.
  3. We access values using user.firstName: it’s not a string, so there’s also no chance of typos.
  4. Our dictionary might contain hundreds of other values alongside "firstName", but our tuple can’t – we must list all the values it will contain, and as a result it’s guaranteed to contain them all and nothing else.

So, tuples have a key advantage over dictionaries: we specify exactly which values will exist and what types they have, whereas dictionaries may or may not contain the values we’re asking for.

There are three other things it’s important to know when using tuples.

First, if you’re returning a tuple from a function, Swift already knows the names you’re giving each item in the tuple, so you don’t need to repeat them when using return. So, this code does the same thing as our previous tuple:

func getUser() -> (firstName: String, lastName: String) {
    ("Taylor", "Swift")
}

Second, sometimes you’ll find you’re given tuples where the elements don’t have names. When this happens you can access the tuple’s elements using numerical indices starting from 0, like this:

func getUser() -> (String, String) {
    ("Taylor", "Swift")
}

let user = getUser()
print("Name: \(user.0) \(user.1)")

These numerical indices are also available with tuples that have named elements, but I’ve always found using names preferable.

Finally, if a function returns a tuple you can actually pull the tuple apart into individual values if you want to.

To understand what I mean by that, first take a look at this code:

func getUser() -> (firstName: String, lastName: String) {
    (firstName: "Taylor", lastName: "Swift")
}

let user = getUser()
let firstName = user.firstName
let lastName = user.lastName

print("Name: \(firstName) \(lastName)")

That goes back to the named version of getUser(), and when the tuple comes out we’re copying the elements from there into individual contents before using them. There’s nothing new here; we’re just moving data around a bit.

However, rather than assigning the tuple to user, then copying individual values out of there, we can skip the first step – we can pull apart the return value from getUser() into two separate constants, like this:

let (firstName, lastName) = getUser()
print("Name: \(firstName) \(lastName)")

That syntax might hurt your head at first, but it’s really just shorthand for what we had before: convert the tuple of two elements that we get back from getUser() into two separate constants.

In fact, if you don’t need all the values from the tuple you can go a step further by using _ to tell Swift to ignore that part of the tuple:

let (firstName, _) = getUser()
print("Name: \(firstName)")
Hacking with Swift is sponsored by Essential Developer

SPONSORED Only until this Sunday, December 12th, you can join a free crash course to learn advanced techniques for testing new and legacy Swift code — it's the fast track to becoming a complete Senior iOS Developer!

Click to learn more

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 5.0/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.