Adding default values to subscript arguments in Swift 5.2

Published on: February 19, 2020

The ability to define custom subscripts in Swift is really powerful. It allows us to write very natural and concise code. Consider the following example of a Grid with a custom subscript:

struct Grid {
  let items : [[GridItem]]

  subscript(x x: Int, y y: Int) -> GridItem? {
    guard !items.isEmpty, (items.startIndex...items.index(before: items.endIndex)).contains(x)
      else { return nil }

    let row = items[x]

    guard !row.isEmpty, (row.startIndex...row.index(before: row.endIndex)).contains(y)
      else { return nil }

    return row[y]
  }
}

Note that subscripts don't use labels by default. To make a subscript use labels, you need to manually declare the subscript label twice like I did in the code above (x x: Int and y y: Int). A subscript like the above would be used as follows:

let item = grid[x: 10, y: 2]

This example works fine in Swift 5.1 and earlier versions in Swift. New in Swift 5.2 is the ability to provide default values for subscript arguments:

struct Grid {
  let items : [[GridItem]]

  subscript(x x: Int = 0, y y: Int = 0) -> GridItem? {
    guard !items.isEmpty, (items.startIndex...items.index(before: items.endIndex)).contains(x)
      else { return nil }

    let row = items[x]

    guard !row.isEmpty, (row.startIndex...row.index(before: row.endIndex)).contains(y)
      else { return nil }

    return row[y]
  }
}

This code is almost the same as the first example, except the subscript now has default arguments. This means that you can use the subscript as follows:

let item = grid[y: 2]

This will automatically call the subscript with a default value of 0 for the x argument.

The fact that it wasn't possible to define default subscript arguments in Swift before was kind of a surprise for me to be honest, but I'm really happy that we have the possibility now. I think it will allow many people to write better subscripts for their objects.

Categories

Quick Tip