- July 29, 2025
- Mins Read
Swift lets you observe and respond to changes in a property’s value by attaching willSet
and didSet
observers to any stored property (except lazy and let
constants).
class TemperatureSensor {
var temperature: Double = 0.0 {
willSet {
print("About to change from \(temperature)° to \(newValue)°")
}
didSet {
let delta = temperature - oldValue
print("Changed by \(delta)°")
if temperature > 100 {
print("Warning: Overheat!")
}
}
}
}
let sensor = TemperatureSensor()
sensor.temperature = 75.0
// About to change from 0.0° to 75.0°
// Changed by 75.0°
sensor.temperature = 120.0
// About to change from 75.0° to 120.0°
// Changed by 45.0°
// Warning: Overheat!
willSet
vs. didSet
willSet
didSet
class Counter {
var count: Int = 0 {
willSet {
print("Counter will change from \(count) to \(newValue)")
}
didSet {
print("Counter did change from \(oldValue) to \(count)")
}
}
}
// Test:
let counter = Counter()
counter.count = 1
// Expect:
// Counter will change from 0 to 1
// Counter did change from 0 to 1
counter.count = 5
// Expect:
// Counter will change from 1 to 5
// Counter did change from 1 to 5
Swift structs and classes get a default memberwise initializer (for structs) or a simple default initializer (for classes without stored-property defaults), but you can—and often should—define your own initializers to enforce invariants or provide convenient defaults.
struct User {
let username: String
var age: Int
var isPremium: Bool
// Custom initializer
init(username: String, age: Int = 18, isPremium: Bool = false) {
self.username = username
// can validate or adjust values if needed
self.age = max(age, 0) // no negative ages
self.isPremium = isPremium
}
}
// Usage:
let guest = User(username: "GuestUser")
// guest.age == 18, guest.isPremium == false
let vip = User(username: "Star", age: 30, isPremium: true)
init()
only if all stored properties have default values.self
in a Method or Initializerinit(name: String) {
self.name = name
}
self
makes it explicit you’re referring to the instance, especially inside closures or nested scopes.struct Rectangle {
var width: Double
var height: Double
// Custom initializer enforcing positive dimensions
init(width: Double, height: Double) {
self.width = max(width, 0)
self.height = max(height, 0)
}
}
// Test:
let r1 = Rectangle(width: 5, height: 3)
assert(r1.width == 5 && r1.height == 3)
let r2 = Rectangle(width: -2, height: 4)
assert(r2.width == 0 && r2.height == 4)
print("Initializer tests passed")
class Circle {
var radius: Double
init(radius: Double) {
self.radius = radius // use of self to disambiguate
}
func scale(by factor: Double) {
self.radius *= factor // self makes it clear we're modifying the property
}
func description() -> String {
// self is optional here, but allowed
return "Circle with radius \(self.radius)"
}
}
// Test:
let circle = Circle(radius: 2)
circle.scale(by: 3)
assert(circle.radius == 6)
print(circle.description()) // "Circle with radius 6.0"
1. Creating Your Own Structs In Swift, a struct is a value type that you define with the struct keyword. ...
1. Trailing Closure Syntax When the last parameter to a function is a closure, you can write that closure after ...
1. What Is a Closure (and Why Swift Loves Them) A closure in Swift is a self-contained block of functionality ...
1. Providing Default Values for Function Parameters (Deep Dive) 1.1 Syntax and Ordering Declaration You assign a default right in ...