Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Swift Computed Property: Code Examples

Computed properties are part of a family of property types in Swift. Stored properties are the most common, saving and returning a stored value, whereas computed ones are a bit different.

A computed property, it’s all in the name, computes its property upon request. It can be a valuable addition to any type to add a new property value based on other conditions. This post explains how and when to use them, as well as the differences between a computed value and a method.

What is a Computed Property in Swift?

A Computed Property provides a getter and an optional setter to indirectly access other properties and values. It can be used in several ways.

An everyday use case is to derive value from other properties. In the following example, we’re creating a filename based on the name and file extension:

struct Content {
    var name: String
    let fileExtension: String
    
    // A computed property to generate a filename.
    var filename: String {
        return name + "." + fileExtension
    }
}

let content = Content(name: "swiftlee-banner", fileExtension: "png")
print(content.filename) // Prints: "swiftlee-banner.png"

As you can see, the filename property is computed by combining both the name and fileExtension properties. Note that we could simplify this code by removing the return keyword:

// A computed property to generate a filename.
var filename: String {
    name + "." + fileExtension
}

The filename property is read-only, which means that the following code would not work:

A Swift Computed Property is read-only, and setters won't work.
A Swift Computed Property is read-only, and setters won’t work.

You could make this more explicit by adding the get wrapper around your computation:

// A computed property to generate a filename.
var filename: String {
    get {
        name + "." + fileExtension
    }
}

However, it’s best practice to simplify the code and remove it as it’s not adding much value if you know what computed properties are (which you do after this post! 😉 )

Using a Swift Computed Property to disclose rich data

Until now, we’ve only covered read-only Computed Properties, while it is also possible to add a setter. A setter can be helpful when you want to prevent access to a more enriched object in your instance.

For example, we could keep our model object private inside a view model while we make a specific property accessible with both its getter and setter:

struct ContentViewModel {
    private var content: Content
    
    init(_ content: Content) {
        self.content = content
    }
    
    var name: String {
        get {
            content.name
        }
        set {
            content.name = newValue
        }
    }
}

var content = Content(name: "swiftlee-banner", fileExtension: "png")
var viewModel = ContentViewModel(content)
viewModel.name = "SwiftLee Post"
print(viewModel.name) // Prints: "SwiftLee Post"

Our Computed Property name serves as our public access point to the inner content value, without revealing too much about our backing model to implementors.

Computed Properties inside an extension

A great thing about Computed Properties is that you can use them inside extensions, too. We could, for example, create quick access to the width and height of a rect:

extension CGRect {
    var width: CGFloat {
        size.width
    }
    
    var height: CGFloat {
        size.width
    }
}

let rect = CGRect(x: 0, y: 0, width: 320, height: 480)
print(rect.width) // Prints: "320"

Overriding Computed Properties

Within subclasses, you can override read-only properties using Computed Properties. This can be useful in custom classes as well as when working with UIKit instances like UIViewController:

final class HomeViewController: UIViewController {
    override var prefersStatusBarHidden: Bool {
        return true
    }
}

We could even make a such property configurable by a backed stored value:

final class HomeViewController: UIViewController {
    
    private var shouldHideStatusBar: Bool = true {
        didSet {
            setNeedsStatusBarAppearanceUpdate()
        }
    }
    
    override var prefersStatusBarHidden: Bool {
        return shouldHideStatusBar
    }
}

Throwing an error from a Swift Computed Property

It’s possible to throw an error in case a computation fails to match a requirement. For example, we could update our filename property and throw an error in case the file extension was found empty:

struct Content {
    enum ContentError: Error {
        case emptyFileExtension
    }
    
    let name: String
    let fileExtension: String
    
    /// A computed property to generate a filename.
    /// Throws an error when the file extension is empty.
    var filename: String {
        get throws(ContentError) {
            guard !fileExtension.isEmpty else {
                throw .emptyFileExtension
            }
            
            return name + "." + fileExtension
        }
    }
}

/// We now need to use the `try` keyword when accessing:
print(try content.filename) // Prints: "swiftlee-banner.png"

Using an async getter

Finally, it’s also possible to define an async get to allow using await inside the getter. For example, we might introduce a backend to validate the filename before returning:

struct Content {
    enum ContentError: Error {
        case emptyFileExtension
        case invalidFilename
    }
    
    var name: String
    let fileExtension: String
    
    // A computed property to generate a filename.
    var filename: String {
        get async throws(ContentError) {
            guard !fileExtension.isEmpty else {
                throw .emptyFileExtension
            }
            
            let filename = name + "." + fileExtension
            let isValidFilename = await backend.validate(filename)
            guard isValidFilename else {
                throw .invalidFilename
            }
            
            return filename
        }
    }
}

/// We now need to use the `try` and `await` keyword when accessing:
print(try await content.filename) // Prints: "swiftlee-banner.png"

This is part of Swift Concurrency, for which you can learn more by following my dedicated course: www.swiftconcurrencycourse.com.

How do you stay current as a Swift developer?

Let me do the hard work and join 27,247 developers that stay up to date using my weekly newsletter:

When should I use a Swift Computed Property?

It’s important to understand when you should use a Swift computed property over a stored property. Use it when:

  • It depends on other properties
  • You’re defining the property inside an extension
  • The property is an access point to another object without disclosing it fully

Although this should help you to decide when to use them, it’s still often a case-by-case decision. My personal preference is to start with a stored value whenever possible. This mostly depends on the above conditions, as well as on whether the property depends on the state. If your computed property derives its value from two static properties, it might be better to create a static property for your derivation, too, to prevent unneeded calculations.

Taking into account performance when defining properties

To explain the previous paragraph in a bit more detail, I’d like to shine a light on performance. A Swift computed property performs its operations every time it’s called. This means that a heavy operation can easily result in a performance decrease.

Let’s explain this using the following code example:

struct PeopleViewModel {
    let people: [Person]
    
    var oldest: Person? {
        people.sorted { (lhs, rhs) -> Bool in
            lhs.age > rhs.age
        }.first
    }
}

let peopleViewModel = PeopleViewModel(people: [
    Person(name: "Antoine", age: 30),
    Person(name: "Jan", age: 69),
    Person(name: "Lady", age: 3),
    Person(name: "Jaap", age: 3)
])
print(peopleViewModel.oldest!.name) // Prints: "Jan"

As you can see, we have a view model for people that contains a collection of Person objects. We have a computed value to get the oldest person in the list.

Every time we access the oldest person, we’re going to sort the collection and return the first element. In this case, we know that we have a static list of people, so we can rewrite this logic and calculate the oldest person once upon initialization:

struct PeopleViewModel {
    let people: [Person]
    let oldest: Person?
    
    init(people: [Person]) {
        self.people = people
        oldest = people.sorted { (lhs, rhs) -> Bool in
            lhs.age > rhs.age
        }.first
    }
}

This way, we know for sure we’re only doing the calculation once, and we could easily gain some performance wins.

Obviously, this is a case-by-case scenario, but it’s essential to consider this when defining your properties. Computation happens on every access, so switch to a stored alternative if these are heavy calculations.

Computed Properties vs Methods in Swift: When to use which?

Another common question is when to decide to use a method instead of a Swift computed property. There’s no common sense in this regard, but there are a few obvious reasons.

Properties, for example, won’t take any arguments. If you want to mock a value for tests or if you want to change one of the input parameters dynamically, you’re likely going to need a method.

I like to take readability and understanding of my code into account when making this decision. I prefer to use methods when the logic I’m about to write involves heavy calculations that may take longer to execute. It’s a way to let implementors be conscious about using logic and prevent performance drains. Properties are tempting to use and are often seen as lightweight values, which makes it a risk if they contain heavy operations.

Conclusion

Computed Properties are part of the basics in Swift. They can be used in many different ways, and it’s essential to know the various use cases. Always take performance into account and try to prevent unneeded calculations if possible.

If you like to learn more tips on Swift, check out the Swift category page. Feel free to contact me or tweet me on Twitter if you have any additional tips or feedback.

Thanks!

 
Antoine van der Lee

Written by

Antoine van der Lee

iOS Developer since 2010, former Staff iOS Engineer at WeTransfer and currently full-time Indie Developer & Founder at SwiftLee. Writing a new blog post every week related to Swift, iOS and Xcode. Regular speaker and workshop host.

Are you ready to

Turn your side projects into independence?

Learn my proven steps to transform your passion into profit.