Subscript Syntax in Swift Explained

Written by LearnAppMaking on April 14 2021 in App Development, Swift

Subscript Syntax in Swift Explained

You use a subscript in Swift to quickly access the member elements of collections. It’s a powerful feature of the Swift Programming Language, because the syntax for subscripts is super concise.

In this tutorial, you’ll learn how to use subscript syntax and how to define your own custom subscripts.

Ready? Let’s go.

  1. What’s a Subscript in Swift?
  2. Subscripts in Practical iOS Development
  3. Create Custom Subscripts
  4. Further Reading

What’s a Subscript in Swift?

It’s easiest to understand what a subscript is by using an example. Check out this dictionary:

var scores = [
    "arthur":   42,
    "ford":     99
    "trillian": 314
]

The above scores dictionary has keys of type String and values of type Int. You could say that it associates person names with a score, i.e. Arthur has scored 42 points.

You can now refer to any of the members in this collection by using subscript syntax. With subscript syntax, you can get a value, as well as set it.

Getting and Setting Values

You get a value like this:

let score = scores["arthur"]
print(score)
// Output: 42

You set a value like this:

scores["arthur"] = 101
print(scores["arthur"])
// Output: 101

The scores["arthur"] code is called subscript syntax. Technically, a subscript is used to access the members of a collection. That really means you can access the values in a dictionary, for example, by using a String key inside square brackets [ ].

Subscript Syntax

A subscript consists of:

  • A variable or constant, containing a collection, such as scores
  • 2 square brackets [ and ]
  • A key or index inside the brackets

Like this:

scores["arthur"] = 101
collection[key] = ···

In Swift, you can use subscripts for arrays, dictionaries, sets, collections, sequences, and even strings. You can also implement your own subscripts with the subscript function, as you’ll see later on.

Subscripts aren’t limited to a single key or “dimension”, so you can use them for multi-dimensional nested arrays and dictionaries as well.

Take this 2×2 matrix, for example:

let matrix = [
    [0, 1],
    [3, 0]
]

You can now access the value 3, like this:

let value = matrix[1][0]
print(value)
// Output: 3

In the above code matrix[1][0], we’re using 2 subscripts successively:

  1. [1] is used to get the array [3, 0] from the array matrix
  2. [0] is used to get the integer 3 from the array [3, 0]

If you’re unfamiliar with arrays, it’s worth reading up on them. Keep in mind that the arrays are zero-indexed, so they start at index number 0.

Give it a try, to see exactly what results you’re getting:

let matrix = [
[0, 1],
[3, 0]
]

print(matrix[1])
print(matrix[1][0])

Subscripts in Practical iOS Development

Subscript syntax is super useful in practical iOS development, because they’re so concise. They make your code easier to read.

Consider the Address Book below. Imagine it’s full of names and addresses.

let contacts = [
    "Arthur Dent": [
        "address": [
            "street": "Infinite Drive 101",
            "postcode": "ABCD12",
            "city": "London"
        ],
        "planet": "Sector ZZ9 Plural Z Alpha",
        "designation": "Mostly Harmless"
        "phone": "+1 1234 56 7890"
    ],
    ···
]

You can use subscript syntax to get to a street address. Like this:

let street = contacts["Arthur Dent"]["address"]["street"]
print(street)
// Output: Infinite Drive 101

It’s super expressive. Get the member with key "Arthur Dent" from contacts, and from that, get the value for key "address", and from that, get the value for key "street". You’re navigating the data, following a deeper branch with every subscript.

It’s important to understand here that the first subscript essentially gets the entire contact entry, and the second gets the "address" collection from that complete entry, and the third gets the String value with the streetname.

You can see it as branches on a tree, if you “rotate” the collection counter-clockwise. Every [···] refers to a branch, until you get to the “leaf”, i.e. the value you’re looking for.

You can use any value for the key in the subscript, including strings, integers, expressions, and other values. As long as the key exists, of course!

Check this out:

let pi = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]

for index in 0..<pi.count
{
let digit = pi[index]

print("\(index + 1)nth digit of Pi = \(digit)")
}

In the above code we’re defining an array with digits of the number Pi (3.1415). The for loop iterates over a range from 0 to the end of the array, with 0..<pi.count.

On the first line of the for loop, pi[index] is used to get an item from the pi array with a given index number. Because of the loop, this index corresponds to every possible index number in the pi array.

Finally, we’re printing out the result:

1nth digit of Pi = 3
2nth digit of Pi = 1
...
10nth digit of Pi = 3

Note: Arrays in Swift start at zero, so we’re using index + 1 to write the right ordinal number. Otherwise it would say “0th digit of …”

Create Custom Subscripts

You can define your own subscripts with the subscript keyword. It’s a function you can add to classes, structs and enumerations.

Here’s an example:

class Database 
{
    subscript(index: Int) -> Object {
        get {
            return database.get(index)
        }
        set(newValue) {
            database.set(index, newValue)
        }
    }
}

Imagine we’re building a database tool, that uses some kind of on-disk storage method. You want to access this database like any other collection, so you define the above subscript function.

You can now read and write from the database like this:

let data = Database()
let entry = data[999]
print(entry)
// Output: <some data>

data[123] = "<some other data>"

A real-world example can be found in the source code of Realm, a database platform. If you scroll down (or search) in Results.swift, you can see the subscript and how it returns an object of type Element from rlmResults. Because of this, you can use Results[index] in your own Swift project.

Subscripts as Functions

What’s even more interesting is that any class (or struct or enum) can define a subscript, even if that class isn’t a typical collection. In that sense, the subscript syntax just calls a function that returns a result.

Here, check this out:

struct Count
{
subscript(index: Int) -> String {
switch index {
case 0: return "zero"
case 1: return "one"
case 2: return "two"
default: return "many"
}
}
}

let count = Count()
print(count[1])
print(count[99])

Hmm, that’s interesting! Even though the Count class isn’t a collection, you can use it like a collection by accessing any key with subscript syntax.

The Int index is just used as the input for the subscript function, which returns a string based on the number by using a switch block. The subscript is read-only though, because it doesn’t make sense here to change any its values.

Multiple Subscript Parameters

You can also create subscripts with multiple parameters. A subscript is similar to a function in Swift, so you can define that subscript(···) in almost any way you want.

Check this out:

struct Matrix<T>
{
    var data:[[T]]

    subscript(x: Int, y: Int) -> T {
        get {
            return data[x][y]
        }
        set {
            data[x][y] = newValue
        }
    }
}

var matrix = Matrix(data: [
    [0, 2],
    [3, 0]
])

matrix[1, 0] = 4

print(matrix[0, 1]) // Output: 2
print(matrix[1, 0]) // Output: 4

Remember the 2×2 matrix we worked with at the start of this tutorial? The above Matrix struct defines a similar matrix. You can initialize a 2-dimensional array with Matrix(data:).

The Matrix struct also uses generics so you can use any data type inside the data array. In the above example, the generic T resolves to the concrete Int type. You could technically also use generics for the array indices, but we’ll leave that here.

Take a look at the subscript(···) function. It has 2 parameters, x and y, both of type Int. Inside the subscript, we’re using those parameters to get to the right position in the 2D data array – just as before. We’re using both set and get, to respectively write and read a value. Awesome!

Note: Getting that annoying “Array index out of range” error? Read here how to fix that: Off-By-One Errors In Swift Programming

Further Reading

In essence, subscripts are just syntactic sugar to access members of collections. It’s quite powerful though, and concise, and it’s always a great idea to dive a little deeper into such syntactic niceties. After all, you learn a great deal about Swift programming this way.

If you’re up for a challenge, check out Playing With Code: Insertion Sort In Swift. The implementation of the insertion sort relies on subscripts to get the right values from the sorting array – so it’s good practice to get the hang of how subscripts and arrays work.

Want to learn more? Check out these resources:

LearnAppMaking

LearnAppMaking

At LearnAppMaking.com, app developers learn how to build and launch awesome iOS apps.