Object Subscripting in Swift

What is Object Subscripting

Subscripts are shortcuts for accessing elements from a collection, sequence or list. They are used to set and retrieve values by index without needing separate methods for setting and retrieval. A type can have multiple subscripts and subscripts can have more than one dimension. To access elements via subscripts write one or more values between square brackets after the instance name.

Two example types that use object subscripting are Array and Dictionary:

var array = [1, 2, 3, 5, 8, 13]

print(array[0])
print(array[1])
print(array[2])
print(array[3])
print(array[4])
print(array[5])

print(array[1..4]) // 1..4 is the range from 1 to 4 without 4 
print(array[1...4]) // 1...4 is the range from 1 to 4 including 4

// prints:
// 1
// 2
// 3
// 5
// 8
// 13
// [2, 3, 5]
// [2, 3, 5, 8]

var dictionary = [
    "We": "❤ Swift",
    "❤": "Swift",
    "Swift": "❤ You"
]

print(dictionary["We"])
print(dictionary["❤"])
print(dictionary["Swift"])

// prints:
// ❤ Swift
// Swift
// ❤ You

How to implement Object Subscripting

The syntax is similar to that of computed properties. You declare a subscript using the subscript keyword and then specifying one or more inputs and the return type. Subscripts can be read-write or read-only.

The general syntax of a subscript:

class YourSubscriptedClass {

    ...

    subscript (<parameters>) -> <return type> {
        // the getter is required
        get {
            <statements>
        }
        // the setter is optional
        set(<setter name>) {
            <statements>
        }
    }
}

Let’s create a simple subscript that outputs true if a string contains a substring and false otherwise.

extension String {
    subscript(pattern: String) -> Bool {
        get {
            let range = self.rangeOfString(pattern)

            return !range.isEmpty
        }
    }
}

var aString = "We ❤ Swift"
aString["❤"] // true
aString["Hello World"] // false

And here we have a similar example that can replace substrings using subscripts:

extension String {
    subscript(pattern: String) -> String? {
        get {
            let range = self.rangeOfString(pattern)

            if !range.isEmpty {
                return pattern
            } else {
                return nil
            }
        }
        set(replacement) {
            let range = self.rangeOfString(pattern)

            if !range.isEmpty {
                self = self.stringByReplacingCharactersInRange(range, withString: replacement!)
            }
        }
    }
}

var aString = "We ❤ Swift"
print(aString["❤"]) // ❤
print(aString["Objective-C"]) // nil

aString["We"] = "You"
print(aString) // You ❤ Swift

Note: you can use newValue instead of replacement in the setter. newValue being implicitly declared in setter functions.

extension String {
    subscript(pattern: String) -> String? {

        ...

        set {
            let range = self.rangeOfString(pattern)

            if !range.isEmpty {
                self = self.stringByReplacingCharactersInRange(range, withString: newValue!)
            }
        }
    }
}

Usage

You can use object subscripting to make code easy to read. For example Swifter uses subscripting to declare routes in an elegant manner.

Create your own DSL for making queries on CoreData models or implement an XPath-like language for getting elements from your user interface. The sky’s the limit.

As NSHipster said “Here be dragons.”, be careful what crazy things you do with subscripting.

Challenges

  • extend the String class and add a subscript(pattern: String) -> String? for pattern matching using regular expressions
  • implement a matrix class
  • implement a chessboard class

If you found this useful please take a moment to share this with your friends :)

  2 comments for “Object Subscripting in Swift

  1. June 16, 2014 at 8:50 pm

Leave a Reply

Your email address will not be published. Required fields are marked *

Subscribe
We send about one email per week with our latest tutorials and updates
Never display this again :)