Leave a rating/review
Notes: 29. Stored Properties
Update Notes: This course was originally recorded in 2019. It has been reviewed and all content and materials updated as of October 2021.
In the Swift Fundamentals course, you created a Student struct that had several properties. The name, grade, and pet were all “stored properties”. They were also instance properties. Which means their values would be stored with the Student instance they belong to.
There’s another sort of property called a type property. You haven’t used that yet, but a type property is stored with the type instead of each individual instance.
In this episode, you’ll learn more about both kinds of properties, as well as some neat tricks, such as how to monitor changes in a property’s value. We’ll also talk about how properties can be used across all of the named types you’ve learned about so far.
Let’s start off with a “Wizard” structure.
32 struct Wizard {
}
A wizard will store a first and last name, both Strings. And, they’ll both be variable, because …wizards can be fickle.
33 var firstName: String
var lastName: String
Now, create a Wizard constant. Mine will be the great Gandalf Greyjoy.
let wizard = Wizard(firstName: "Gandalf", lastName: "Greyjoy")
After you’ve created an instance, you can access the properties with a dot and then the property name.
wizard.firstName
wizard.lastName
Because this is a Structure, to change the value of those properties we would need to make our wizard variable.
😺var 🛑wizard =
And then they could freely change their name to something like…
wizard.firstName = "Hermione"
wizard.lastName = "Kenobi"
wizard
Hermione Kenobi.
We can see that worked by clicking on the grey rectangle in sidebar.
You’ve made stored properties like this before! But there’s actually a bit more to them: a feature called “property observers”. To start, add a pair of curly braces right after the property type.
var firstName: String 😺{
}
Then, there are two kinds of property observers: willSet and didSet.
34 willSet {
}
didSet {
}
The code you use for willSet, as the name suggests, is called right before a new value is stored. You can either name the argument for the new value, yourself, or use the default, which is “newValue”.
print(firstName + " will be set to " + newValue)
Run the playground and you can see from the printout that at this point, we can still access the value that’s about to change, using the name of the property we’re observing. Which in this case is firstName.
didSet is a little bit more powerful. With didSet, you can actually make changes to a property you’re observing.
To see how that might be useful, first check to see if the value that just got set contains a space.
if firstName.contains(" ") {
}
You access the current value, which just got set, using firstName. The previous value, by default, gets passed in as oldValue. So, let’s write a message about what we’re going to do, when the value getting set contains a space, and is therefore not going to be considered a first name.
print("No spaces allowed! \(firstName) is not a first name. Reverting name to \(oldValue).")
To actually revert the name, like we’re saying we’re going to, we can assign oldValue to the firstName property.
firstName = oldValue
Let’s test that out, by assigning something that looks more like a full name, to the wizard’s first name.
wizard.firstName = "Merlin Rincewind"
And run the playground… Notice how the last thing we see get printed out, is from our didSet observer. Even though firstName is getting reassigned, neither the willSet nor didSet observers are getting called again. That’s probably the behavior that you want, so it’s good that Swift works that way. Just be aware of it.
You might be wondering if property observers run when you first initialize a Wizard instance. Even if you’re just using the memberwise initializer that’s generated for you, you are assigned a firstName which has property observers.
But property observers only get called after initialization. If you think about it, that makes sense. Before initialization, the properties have no values, so you’d only have half the amount of data to work with as usual. oldValue, for example, would have no meaning, in didSet.
Sure, Swift could have gone for using optionals, but instead, things are simpler. Property observers just don’t get called in initializers. And that means property observers are only useful for variable properties, since constant properties can only be set during initialization.
Now, let’s cover one more kind of property: Type Properties. Right now, all of our properties are related with a single wizard in particular. In Swift, you’d say “an instance of a Wizard”, with “instance properties”. Those makes perfect sense, for names.
But if you have a property that would be the same for every instance, you might want to consider making it a property of the type itself instead. What if there were common magical ingredients that every wizard has? Maybe they get access to them when they get their professional wizarding license.
let commonMagicalIngredients = [
"Polyjuice Potion",
"Eye of Haystack Needle",
"The Force"
]
You could make those ingredients an instance constant, like that. But if you add the keyword static, then they would be properties of the wizard type.
😺static 🛑let commonMagicalIngredients = [
- To access them, you’d just use the name of the type, instead of the name of an instance. (And that normally means you’re starting with a capital letter instead of lowercase.)
Wizard.commonMagicalIngredients
Now, as you see, you can have a static constant. But you can also use a variable.
static var commonMagicalIngredients = [
And then you can change the value of that type property
Wizard.commonMagicalIngredients😺.append("Wow-Wow Sauce")
Pretty much all the same tools are available, whether you’re working with instance or type properties! So, you could also give it property observers, if you wanted.
"The Force"
]😺 {
didSet {
print("Magical Ingredients updated! Common stock now contains \(commonMagicalIngredients)")
}
}🛑
Using a type property means you can retrieve the same stored property value from anywhere in the code for your app.
Everything you’ve learned about properties so far applies equally to Structs and Classes. Enumerations are a little more restricted. They cannot have stored instance properties, only stored type properties. They can, however, have computed instance properties. And we’ll learn about computed properties, next!