Swift Class Properties
Properties are essential components of classes in Swift that allow you to store and compute values. Unlike simple variables, properties come with additional capabilities that make Swift's object-oriented programming powerful and flexible.
Introduction to Class Properties
Properties in Swift classes represent the attributes or characteristics of the objects created from those classes. They provide a way to access and modify the data associated with a class instance.
Swift offers several types of properties:
- Stored properties
- Computed properties
- Property observers
- Type properties
Let's explore each type with examples and practical applications.
Stored Properties
Stored properties are the most basic form of properties that simply store a value as part of an instance of a class.
Basic Syntax
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
// Creating an instance
let person = Person(name: "Alice", age: 28)
print(person.name) // Output: Alice
print(person.age) // Output: 28
Constant and Variable Stored Properties
Swift allows you to declare stored properties as either constants (let
) or variables (var
):
class Car {
let manufacturer: String // Constant property - cannot be changed after initialization
var currentSpeed: Double // Variable property - can be modified
init(manufacturer: String) {
self.manufacturer = manufacturer
self.currentSpeed = 0.0
}
}
let myCar = Car(manufacturer: "Toyota")
// myCar.manufacturer = "Honda" // This would cause an error
myCar.currentSpeed = 60.5 // This is allowed
Default Values
You can provide default values for stored properties:
class Counter {
var count: Int = 0 // Default value
var maxValue = 100 // Type inference with default value
func increment() {
if count < maxValue {
count += 1
}
}
}
let myCounter = Counter()
print(myCounter.count) // Output: 0
myCounter.increment()
print(myCounter.count) // Output: 1
Computed Properties
Computed properties don't actually store a value; instead, they provide a getter and an optional setter to retrieve and set other properties indirectly.
Basic Syntax
class Rectangle {
var width: Double
var height: Double
// Computed property
var area: Double {
return width * height
}
init(width: Double, height: Double) {
self.width = width
self.height = height
}
}
let rectangle = Rectangle(width: 5.0, height: 3.0)
print(rectangle.area) // Output: 15.0
Getters and Setters
Computed properties can have both getters and setters:
class Temperature {
var celsius: Double
// Computed property with getter and setter
var fahrenheit: Double {
get {
return (celsius * 9/5) + 32
}
set {
celsius = (newValue - 32) * 5/9
}
}
init(celsius: Double) {
self.celsius = celsius
}
}
let temp = Temperature(celsius: 25.0)
print(temp.fahrenheit) // Output: 77.0
temp.fahrenheit = 86.0
print(temp.celsius) // Output: 30.0
Read-Only Computed Properties
If you only provide a getter but no setter, you have a read-only computed property:
class Circle {
var radius: Double
// Read-only computed property
var area: Double {
return Double.pi * radius * radius
}
init(radius: Double) {
self.radius = radius
}
}
let circle = Circle(radius: 2.0)
print(circle.area) // Output: 12.566370614359172
// circle.area = 20 // Error: Cannot assign to property: 'area' is a get-only property
Property Observers
Property observers let you observe and respond to changes in a property's value. You can add observers to any stored property, and they're called every time a property's value is set.
willSet and didSet
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// Output:
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// Output:
// About to set totalSteps to 360
// Added 160 steps
Practical Example with Property Observers
class BankAccount {
var accountName: String
var balance: Double {
didSet {
if balance < 0 {
print("WARNING: Negative balance detected!")
}
if balance != oldValue {
let change = balance - oldValue
let action = change > 0 ? "deposit" : "withdrawal"
let amount = abs(change)
transactionHistory.append("\(Date()): \(action) of $\(amount)")
}
}
}
var transactionHistory: [String] = []
init(accountName: String, initialBalance: Double) {
self.accountName = accountName
self.balance = initialBalance
}
func printTransactions() {
for transaction in transactionHistory {
print(transaction)
}
}
}
let account = BankAccount(accountName: "John's Savings", initialBalance: 100.0)
account.balance += 50.0
account.balance -= 75.0
account.printTransactions()
// Output will include two transaction records
Lazy Properties
Lazy stored properties are properties whose initial value isn't calculated until the first time it's used. This is useful when the initial value for a property is dependent on outside factors, or when it's computationally expensive to set up.
class DataManager {
lazy var data: [String] = {
// This could be an expensive operation like loading a file
print("Initializing data array...")
return ["One", "Two", "Three"]
}()
}
let manager = DataManager()
print("DataManager initialized")
// At this point, the data property hasn't been initialized yet
print(manager.data) // Now the data property gets initialized
// Output:
// DataManager initialized
// Initializing data array...
// ["One", "Two", "Three"]
Type Properties
Type properties belong to the type itself, not to instances of that type. They're useful for defining properties that are common to all instances of a particular type.
Static Properties
class SomeClass {
static var storedTypeProperty = "Some value"
static let constantTypeProperty = "Another value"
static var computedTypeProperty: Int {
return 42
}
}
print(SomeClass.storedTypeProperty) // Output: Some value
print(SomeClass.computedTypeProperty) // Output: 42
Practical Example of Type Properties
class TemperatureConverter {
static let celsiusZeroInFahrenheit = 32.0
static let celsiusBoilingInFahrenheit = 212.0
// Type method to determine if water would freeze at a given temperature
static func willWaterFreeze(atFahrenheit fahrenheit: Double) -> Bool {
return fahrenheit <= celsiusZeroInFahrenheit
}
}
print("Water freezing point: \(TemperatureConverter.celsiusZeroInFahrenheit)°F")
print("Will water freeze at 30°F? \(TemperatureConverter.willWaterFreeze(atFahrenheit: 30))")
// Output:
// Water freezing point: 32.0°F
// Will water freeze at 30°F? true
Real-World Application Example
Let's build a more complex example that combines different types of properties to model a real-world system:
class Student {
let id: String
var name: String
private var _grades: [Double] = []
// Computed property for read-only access to grades
var grades: [Double] {
return _grades
}
// Computed property for GPA
var gpa: Double {
if _grades.isEmpty {
return 0.0
}
let sum = _grades.reduce(0, +)
return sum / Double(_grades.count)
}
// Property with observers
var isHonorRoll: Bool = false {
didSet {
if isHonorRoll && !oldValue {
print("\(name) has made the honor roll!")
}
}
}
// Type property for requirements
static let honorRollThreshold = 3.5
init(id: String, name: String) {
self.id = id
self.name = name
}
func addGrade(_ grade: Double) {
_grades.append(grade)
// Check honor roll status
isHonorRoll = gpa >= Student.honorRollThreshold
}
}
// Using the Student class
let alice = Student(id: "A12345", name: "Alice Johnson")
alice.addGrade(3.7)
alice.addGrade(3.9)
alice.addGrade(3.8)
print("\(alice.name)'s GPA: \(alice.gpa)")
print("Grades: \(alice.grades)")
// Output:
// Alice Johnson has made the honor roll!
// Alice Johnson's GPA: 3.8
// Grades: [3.7, 3.9, 3.8]
Summary
Swift class properties are powerful tools that allow you to define the characteristics and behaviors of your classes:
- Stored properties hold values that are part of the instance
- Computed properties calculate values on-the-fly based on other properties
- Property observers respond to changes in property values
- Lazy properties defer initialization until first access
- Type properties belong to the class itself rather than instances
Understanding these different types of properties enables you to write more expressive, flexible, and powerful Swift code.
Exercises
- Create a
BankAccount
class with a balance property that doesn't allow negative values. - Design a
Circle
class with a radius property and computed properties for area, diameter, and circumference. - Build a
Team
class with a property observer that tracks changes in the team roster. - Implement a
Calculator
class with type properties for mathematical constants. - Create a
Book
class with lazy properties for content that is only loaded when needed.
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)