Leave a rating/review
Notes: 30. Computed Properties
Update Notes: This course was originally recorded in 2019. It has been reviewed and all content and materials updated as of October 2021.
Stored properties will keep their value. Unless you change it, the value will be the same every time you access the property.
There’s another sort of property calculates its value on-the-fly, each time you access it: a computed property. Let’s revisit our Wizard struct to try one out.
Another property of a wizard could be their full name. The first and last name are already stored properties, so we don’t need to store the full name too. Instead, it can be a computed property.
A computed property always begin with var.
var
let wouldn’t make sense, because you’re always going to return a value that’s created when you use the computed property. After var, you decide on a name for the property, then you give it an explicit type.
var 😺fullName: String
Afterwards, you use a set of curly braces, and within them, you write some code that will return the explicit type–in this case, a String.
36 var fullName: String 😺{
return ""
}
It’s just like writing a function that doesn’t take any arguments. The actual result we want, for a “full name”, is a String thats starts with the wizard’s first name, then has a space, and ends with their last name.
return "\(firstName) \(lastName)"
Now, you can use the fullName property on your wizard!
42 wizard.fullName
As it is, fullName is a “get-only” computed property. “Read-only” is another term you’ll hear for the same idea. Try to assign to wizard.fullName, and you’ll an error.
42 wizard.fullName = ""
We’re actually using a bit of shorthand right now. The code that’s running for fullName is found in what’s called a “get accessor”, or “getter”.
If you don’t use the shorthand, it looks like this, with the keyword get, and the code wrapped in another set of braces:
var fullName: String {
get { return "\(firstName) \(lastName)" }
}
To make it so that we can also set fullName, let’s create a set accessor!
38 set {
}
It starts off just like get. The difference is, instead of returning a value, you’re using a value coming in as a parameter, and not returning anything.
You can give a name to that parameter, in parentheses. newFullName would be a good, accurate name for it.
38 set(newFullName) {
We can separate the first and last names in newFullName using the split method, with a space as the separator.
let nameSubstrings = newFullName.split(separator: " ")
Calling split on a String returns an array, but its elements are not Strings. They’re Substrings, which represent parts of the original string. This will come into play after a guard statement, in which we make sure that there are at least two name substrings (representing the wizard’s first and last name).
41 guard nameSubstrings.count >= 2 else {
return
}
If not, print out that the new value doesn’t meet the requirement for being a full name.
guard nameSubstrings.count >= 2 else {
😺print("\(newFullName) is not a full name.")🛑
return
“SeverusWenderlich”, without any spaces…
52 wizard.fullName = 😺"SeverusWenderlich"🛑
…is not a full name! You don’t actually need to come up with your own name for the parameter used by set. By default, it will be called “new value”, which is generally just fine.
You can delete the parameter and parentheses, and then use newValue instead of newFullName.
set❌(newFullName)❌ {
let nameSubstrings = 😺newValue🛑.split(separator: " ")
guard nameSubstrings.count >= 2 else {
print("\(😺newValue🛑) is not a full name.")
return
}
}
Now, to set the first and last name based on this new full name, we need a String array instead of substrings. To get that, you could use a for loop to populate an array. Or, you could map the substrings, like this:
return
}
let nameStrings = nameSubstrings.map { substring in
String(substring)
}
There’s actually a shorter way to write that that doesn’t involve any dollar signs.
46 let nameStrings = nameSubstrings.map(String.init)
That’s exactly like passing an existing function into map for the argument. The function, in this case, is just the appropriate String initializer.
The end result is that nameStrings contains actual, Strings, in the order they appear in newValue. Within the scope of “set”, you have access to all instance members. If they’re variable, you can give them new values.
47 firstName = nameStrings.first!
lastName = nameStrings.last!
Force unwrapping is fine here, because, due to the guard statement, we know that nameStrings contains a first and last name. Now, if you have a space in the String you use for attempting to set your wizard’s full name…
wizard.fullName = "Severus😺 🛑Wenderlich"
…their first and last names will change, as expected! You might be wondering if you can have a set-only property.
You cannot!
That’s not to say it wouldn’t ever make sense to have that, but Swift doesn’t support it, like some other programming languages do.