What is a Computed Property in Swift?

Computed properties are part of a family of property types in Swift. Stored properties are the most common which save and return 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 of your types 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?

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

A common 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 property. 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:

Computed Properties are readonly and setters wont work.
Computed Properties are readonly and setters wont 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 Computed Property to disclose rich data

Until now we’ve only covered read-only Computed Properties while it is possible to also add a setter. A setter can be useful 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 is now our public access point to the inner content value without letting implementors know too much about our backing model.

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 view:

extension UIView {
    var width: CGFloat {
        return frame.size.width
    }
    
    var height: CGFloat {
        return frame.size.width
    }
}

let view = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
print(view.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
    }
}

When should I use a Computed Property?

It’s important to understand when you should use a 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 per case decision. My personal preference is to try and start with a stored value if possible. This mostly depends on the above conditions as well as on the fact 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 on performance. A 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 per case scenario but it’s important to think about this when you’re defining your properties.

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

Another common question is when to decide to use a method instead of a 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 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 if the logic I’m about to write contains heavy calculations that potentially 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 it contains heavy operations.

Conclusion

Computed Properties are part of the basics in Swift. They can be used in many different ways and it’s important to know the different 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!