Properties and Property Wrappers
13.1 Stored, Computed and Lazy Properties
struct Rectangle {
var width: Double // stored
var height: Double
var area: Double { width * height } // computed (read-only)
var perimeter: Double {
get { 2 * (width + height) }
set { width = newValue / 4; height = newValue / 4 } // computed setter
}
}
class DataLoader {
lazy var cache: [String: Data] = loadCache() // computed on first access
private func loadCache() -> [String: Data] { [:] }
}13.2 Property Observers
class StepCounter {
var steps: Int = 0 {
willSet { print("About to set to \(newValue)") }
didSet {
if steps > oldValue { print("Added \(steps - oldValue) steps") }
}
}
}13.3 Type Properties
struct AppConfig {
static let apiVersion = "v2" // shared by the type (lazily, thread-safely initialized)
static var requestCount = 0
static var baseURL: String { "https://api.example.com/\(apiVersion)" }
}
class Theme {
class var accentColor: String { "blue" } // overridable in subclasses
}13.4 Property Wrappers
Familiar wrappers: SwiftUI's @State, @Binding, @Environment, @AppStorage, SwiftData's @Query, swift-argument-parser's @Option/@Flag.
@propertyWrapper
struct Clamped<Value: Comparable> {
private var value: Value
let range: ClosedRange<Value>
init(wrappedValue: Value, _ range: ClosedRange<Value>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
var wrappedValue: Value {
get { value }
set { value = min(max(newValue, range.lowerBound), range.upperBound) }
}
var projectedValue: ClosedRange<Value> { range } // accessed with $
}
struct Volume {
@Clamped(0...100) var level: Int = 50
}
var v = Volume()
v.level = 150
print(v.level, v.$level) // 100 0...100
@propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
var wrappedValue: T {
get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue }
set { UserDefaults.standard.set(newValue, forKey: key) }
}
}
struct Settings {
@UserDefault(key: "darkMode", defaultValue: false) static var darkMode: Bool
}