Computed Properties Explained in Swift
A computed property is a property that calculates and returns a value, rather than just store it. In this tutorial, we’ll discuss how computed properties work and how you can use them in your day-to-day iOS development.
Here’s what we’ll get into:
- What’s a computed property?
- How read-only computed properties work
- Stored vs. computed properties
- How to calculate a value with a computed property
- Getters and setters – how they work, and why you need ’em
- Practical examples of computed properties in iOS development
Ready? Let’s go.
- What’s A Computed Property?
- Getters & Setters for Computed Properties
- Read-Only Computed Properties
- Practical Use Cases for Computed Properties
- Further Reading
What’s A Computed Property?
Before we discuss computed properties, let’s do a quick recap of what properties are. Here’s a quick summary:
- A property associates a value with a name, like a variable, and it’s a part of a class, structure or enumeration.
- A stored property stores constant and variable values as part of a class (or struct) instance. The
name
inuser.name = "John"
is a stored property, anduser
is an instance of a class. - It’s easiest to think of stored properties as variables that are attached to instances of a class or struct. They help you organize your code better, based on the principles of OOP.
Let’s move on. A computed property calculates a value, rather than store it. Here’s an example:
struct Rectangle
{
var width:Double
var height:Double
var area:Double {
width * height
}
}
In the above Swift code, we’ve defined a struct called Rectangle
. It has two properties width
and height
of type Double
. The third property, called area
of type Double
, is a computed property. Here it is once more:
var area:Double {
width * height
}
The above area
property executes the code width * height
, and return its value, when it’s is called. Here, check this out:
let square = Rectangle(width: 12.0, height: 12.0)
print(square.area)
In the above code, we’ve defined a square with a width and height of 12 units. We then calculate the surface area of the square by referencing the area
property of square
, and print out its result. Internally, the code width * height
is called, i.e. 12.0 * 12.0 = 144.0
, and returned.
If we had defined the area
property as a function, it would have looked something like this:
func area() -> Double {
return width * height
}
See how that works? A computed property is kinda like a function, but different. Computed properties have a dense, concise syntax – and when used well, they make for more expressive and clearer code.
Note: In the above example, for area
, we’ve created a read-only computed property, which uses an implicit return. More on that, below!
Getters & Setters for Computed Properties
OK, so far we’ve established that a computed property calculates a value. They execute some code when you call ’em, and they’re different from stored properties and functions. So far so good!
Computed properties can also provide a custom getter and setter.
- A getter is executed when the property is read/retrieved
- A setter is executed when the property is set/changed
Let’s take a closer look with an example:
struct User
{
private var firstName = ""
private var lastName = ""
var name:String {
get {
return firstName + " " + lastName
}
set(newValue) {
let split = newValue.components(separatedBy: " ")
firstName = split[0]
lastName = split[1]
print("firstName = \(firstName), lastName = \(lastName)")
}
}
}
var user = User()
user.name = "John Doe"
print(user.name)
In the above code, we’ve created a User
struct with two private properties firstName
and lastName
of type String
. The third property is computed, called name
of type String
.
The computed property name
has a custom getter and setter defined. Here’s a quick overview of the syntax we use for that:
var property:type {
get {
code
}
set(value) {
code
}
}
As part of the computed property, we can specify what happens when a property is read (getter) or changed (setter). Within the getter, you’re supposed to return a value. Within the setter, you’re supposed to store or change some value.
In the previous code sample, here’s what happens:
- We’ve defined a
User
struct that has a private implementation for thefirstName
andlastName
properties. We can’t change those from the outside, but only through thename
property. - When you read the
name
property, the code forget { ··· }
is executed. You get the value of thefirstName
andlastName
strings with a space in between. - When you change the value of
name
, the code forset { ··· }
is executed. In the code, the provided stringnewValue
is split into components, and assigned tofirstName
andlastName
.
We can use the local newValue
constant as the value that’s provided, so we have access to both the new and the current values. This newValue
constant is implicitly available inside the setter, so you don’t have to declare it explitly. You can, however, provide your own constant name, if you want. Like this:
set {
// Use `newValue` in here (implicit)
}
set(newString) {
// Use `newString` in here (explicit)
}
Quick tip: For the sake of this example, we’re assuming that all person’s first and last names are separated with one space character. This isn’t true in the real world, of course – names come in all shapes and sizes. Yay!
Read-Only Computed Properties
We’ve looked at getters and setters for computed properties in the previous sections, but what about a property that only has a getter? That’s a read-only computed property; it can only be read, and not set.
Here, check this out:
struct Circle
{
var radius:Double
var circumference:Double {
2 * .pi * radius
}
}
In the above code, we’ve defined a struct Circle
that has a stored property radius
and a computed property circumference
, both of type Double
. We can use it like this:
let earth = Circle(radius: 6371)
print(earth.circumference) // Output: 40030.173592041145
That circumference
property is computed and read-only – it only has a getter. In fact, the above declaration of circumference
is exactly the same as this:
var circumference:Double {
get {
return 2 * .pi * radius
}
}
Comparing it against the previous code sample, you see two differences:
- The
get { ··· }
part is removed. When left out, Swift assumes we’re declaring a read-only computed property. - There’s no
return
statement. Since Swift 5.1, single-line expressions can omit an explicitreturn
for the sake of clarity, brevity and expressiveness.
Awesome! Let’s look at a few real-world scenarios for computed properties, next.
Practical Use Cases for Computed Properties
When you think about it, computed properties are just functions without parameters. Aren’t they? In essence, the whole of programming is just syntactic sugar and “structure” around 1’s and 0’s… Merely saying “Computed properties are just functions without ()
_”_ misses the whole point!
Let’s look at a few real-world use cases.
First, the read-only computed property is ideal for expressing simple calculated values concisely. Consider the circumference of a circle, that we’ve discussed before. You can, for example, provide a person’s formatted address based on individual values. Like this:
struct Person {
var street:String
var streetNumber:String
var city:String
var postcode:String
var address:String {
"\(street) \(streetNumber)\n\(postcode), \(city)"
}
}
let bob = Person(···)
print(bob.address) // Output: Infinite Lane 42 12A34B, Diamond City
More specifically, the above properties make sense from a stored database perspective. The address
property is essentially a view into the model data of Person
. You don’t have to store the complete address, just its individual components. You can them present them in any way you want.
Secondly, you can use computed property getters and setters to adjust other values than a property itself. Here, check out this example:
struct RentalCar
{
var costPerDay:Double
var costPerWeek:Double {
get {
costPerDay * 7.0
}
set {
costPerDay = newValue / 7.0
}
}
}
var toyota = RentalCar(costPerDay: 12.0)
toyota.costPerWeek = 100.0
print(toyota.costPerDay) // Output: 14.28
In the above code, we’re creating a struct RentalCar
that has a two properties:
- A stored property
costPerDay
of typeDouble
- A computed property
costPerWeek
of typeDouble
We could locally say that the cost per week of a rental is the cost per day times 7, and vice versa. In the above code, you see that the costPerDay
is the absolute determining factor for renting a car for any length of time.
- When
costPerWeek
is set, we divide it by 7 and write it tocostPerDay
. - When
costPerWeek
is read (“get”), we multiply it by 7 to get the cost per week.
In your development, you can now work with either day or week units, thanks to computed properties. Your code is, as far as this example goes, more concise and easier to read. The ratio between days and weeks is also an implementation detail of the RentalCar
struct itself, so your interfacing code doesn’t have to worry about mediating between the two. Awesome!
Further Reading
In this tutorial, we’ve discussed how to work with computed properties in Swift. We’ve looked at read-only properties, getters and setters, and practical examples in real-world iOS development.
Want to learn more? Check out these resources:
- Properties in Swift Explained
- Initializers & init() Explained In Swift
- Struct vs. Class In Swift Explained
- Classes in Swift Explained
- Introduction Of Object-Oriented Programming In Swift
- How To: Lazy Computed Properties In Swift
- Getting Started With Realm Database In Swift
- Functions In Swift Explained
- The Ultimate Guide To Closures In Swift