You use properties in Swift to tack some data onto an object, like cookie.flavor = "Chocolate Chip". Properties in Swift look unassuming, but they actually pack quite a punch! In this tutorial, we’re going to discuss common aspects of working with properties. Here’s what we’ll get into:
- What’s a property and how do you use it?
- Stored properties vs. computed properties
- what happens when a property is set, with a setter
- the return value of a property, with a getter
- Observing property value changes with willSet and didSet
Table of Contents
What’s a Property?
A property is a value that’s connected to an object, like a class, struct or enum. You can think of them as “sub-variables”, i.e. a variable that’s part of another object. Let’s take a look at an example: struct Cookie { var flavor: String = "" } var choco = Cookie() choco.flavor = "Chocolate Chip" In the above code, we’ve defined a struct called Cookie. It has one property called flavor of type String, whose initial value is an empty string. The syntax to declare a (stored) property is: var name: type = value. You do this within the class or struct, just as in the above example. You can also use let. The syntax is similar to that of declaring a variable or constant. In the next few sections we’ll discuss more approaches for working with properties. In the second part of the above example code, we’ve initialized a Cookie object and assigned it to the choco variable. On the last line, we’re assigning a string value to the flavor property of the choco object. You see that there are 2 parts to working with a property:- Declaring the property inside a struct, with var flavor
- Assigning a value to the property, on an object, with choco.flavor =
Access Control
Speaking of inside/outside – what about access control? You can determine if a property can be read from outside a struct or class, or only from inside it. You do this by placing one of these keywords before the property declaration.- open and public: anyone can access, within the module, and in code that imports the module
- internal: anyone can access, but only within the module (default)
- : anyone can access, but only within the current Swift file
- private: only the enclosing declaration can access, such as a class or struct
Static Properties
You can also create static properties, by prepending the property declaration with the static keyword. A static property is available on the type of the struct (or class) itself, as opposed to an instance of that struct. As such, it’s often referred to as a “class property” and not an instance property. Here’s an example: struct Cookie { static var cookienessLevel = 0 } Cookie.cookienessLevel = 9000 print(Cookie.cookienessLevel) // Output: 9000 Awesome! Let’s move on to stored and computed properties. Both the self keyword and access control are important concepts in Swift programming. You can learn more about them in Access Control Explained In Swift and Self and self in Swift.Stored and Computed Properties
Properties come in 2 major flavors: stored properties and computed properties. A quick summary:- Stored property: A stored property merely stores a value. It has a type, and you can get/read and set/write the property. It’s as simple as that!
- Computed property: A computed property returns a value based on some calculation. They’re read-only, unless you use a getter and setter (see below).
Property Getters and Setters
A computed property always defines a getter, and optionally defines a setter. In short, a getter is the code you use to get (“read”) a value from a property, and a setter is the code you use to set (“write”) a property to a new value. You can change the way that happens by customizing the getter and setter. Let’s take a step back and look at how getters and setters work for stored properties. They don’t do anything else than storing and retrieving the value. Like this: // Setting a value cookie.flavor = "Chocolate Chip" // Getting a value print(cookie.flavor)GetterWhen you create a computed property, you’ll always provide a custom getter for that property. After all, you’re changing the way the property provides a value – with a computation. Check out this computed property: var area: Double { width * height } That’s exactly the same as this: var area: Double { get { width * height } } With the get { } code, we’ve indicated the code that needs to be executed to get/read a value from the area property. Because a computed property at least returns a value, this getter is implied when you don’t provide the get { } block.SetterWhat about setters? They customize what happens when you change the value of the property. Here’s a comprehensive example: struct Temperature { var celcius: Double = 0.0 var fahrenheit: Double { get { (celcius * 9.0/5.0) + 32.0 } set(newValue) { celcius = (newValue - 32.0) * 5.0/9.0 } } } // 20 °C -> 68 °F var temp = Temperature() temp.celcius = 20 print(temp.fahrenheit) // 86 °F -> 30 °C temp.fahrenheit = 86 print(temp.celcius) In the above code we’ve defined a structure Temperature with a stored property celcius and a computed property fahrenheit, both of type Double. The value of fahrenheit is based on that of celcius, so the celcius property is storing the actual temperature data. You can see the getter for fahrenheit with get { }, and you can also see the setter with set { }. They’re almost the same, except that the setter has an additional value that’s available within it: newValue. It’s the new value that’s assigned to the computed property, when the setter is invoked. In fact, you can name that value anything you want! You can even omit newValue altogether, and still use it inside the setter. Think of “newValue” as the “input” for the setter. When the code temp.fahrenheit = 86 is executed, 86 is the value that’s available as newValue within the setter. You can also see that the setter of a computed property always involves another property, such as celcius. You cannot use a property’s value within its own getter or setter, because that’d cause an infinite loop. Here, check this out: struct Cookie { private var _flavor: String = "" var flavor: String { get { return _flavor } set(newFlavor) { _flavor = newFlavor } } } var cookie = Cookie() cookie.flavor = "Chocolate Chip" print(cookie.flavor) // Output: Chocolate Chip What’s going on here? If you look closely, you’ll see that we’ve defined a Cookie struct with two properties _flavor and flavor. The latter is a computed property that’ll store a value in the former. The computed property flavor will get/set from the private stored property _flavor. We’ve essentially recreated a stored property using a computed property. This doesn’t make any sense; it’s not code you’d use during everyday programming. Still, it’s pretty neat – and it goes to show that getters and setters are just syntactic sugar around the storing/retrieving of data.Property Observers: willSet and didSet
Before you go, let’s discuss one last powerful aspect of properties: property observers. Like the name implies, you can use property observers to respond to value changes of a property. Here’s how that works:- Before a property changes, its willSet observer is invoked (with the new value as a parameter)
- After a property changes, its didSet observer is invoked (with the old value as a parameter)
- In willSet, you can use the about-to-be-set value called newValue
- In didSet, you can use the previous value called oldValue
- In both property observers you can use the current value, e.g. amount
- You can add willSet and didSet to stored properties that you define and inherit, and to computed properties you inherit. If you’ve defined your own computed property, it’s common to add code that observes it to the setter instead of an observer. So you may not need didSet or willSet for your specific use case!
- If you’re setting properties in a custom initializer, those properties’ willSet and didSet aren’t called. The property observers of a superclass you inherit are executed, however, once the superclass initializer has been called. (You’ll know it when you see it. It’s good to know that observers behave tricky around initializers.)
Further Reading
Properties look so simple, but there’s a lot of complexity behind some simple code like cookie.flavor = "Chocolate Chip". You’ve got plenty of opportunities to customize how your properties behave. Here’s what we discussed:- What a stored property is and how you use it
- Stored properties vs. computed properties
- Getters and setters with get and set(newValue)
- Property observers with willSet and didSet
Related Articles
- 20 Ways to Increase Sales in Your Online Pet Store
- Guide to Custom Apple Watch Faces & DIY App Development
- Unique Website Ideas for Your Inspiration in 2021
- 5 Effective Instagram Automation Ideas to Grow Your Followers
- What is 24/7 Support? Definition, Benefits, Tips
- Snapchat vs. Instagram [Difference between Snapchat and Instagram]
- The Basics of App Store Optimization
- Benefits and Importance of Non-Fungible Tokens (NFTs)
- 10 Smart Ways to Grow Your Business Using LinkedIn
- The Comprehensive Guide To Call Centers: Discover Types, Roles And Tech Advancements