← thecodex.expert · The Codex Family of Knowledge
Swift

Property Wrappers

SwiftUI’s @State isn’t special, built-in compiler magic — it’s an ordinary struct using @propertyWrapper, the exact same mechanism available for any custom wrapper written from scratch.

Swift 6 Same mechanism as @State Last verified:
Canonical Definition

@propertyWrapper struct Name { var wrappedValue: T { get set } } defines a wrapper — applying @Name to a property routes every read and write through wrappedValue's getter/setter instead of storing the value directly, letting the wrapper add validation, persistence, or transformation logic transparently. An optional projectedValue (accessed via $propertyName, as in $count for a property wrapped with @State) exposes additional functionality beyond the wrapped value itself, such as a Binding. This is the exact, non-magical mechanism SwiftUI's @State and Combine's @Published are built from — any project can define an equally real, reusable wrapper the same way.

Writing a custom property wrapper: Clamped

Every assignment to value routes through this custom setter — the clamping happens transparently, with the property looking like an ordinary Int everywhere it's used.

Swiftclamped_wrapper.swift
@propertyWrapper
struct Clamped {
    private var value: Int
    let range: ClosedRange<Int>

    var wrappedValue: Int {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }

    init(wrappedValue: Int, _ range: ClosedRange<Int>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }
}

struct Volume {
    @Clamped(0...100) var level: Int = 50
}

var v = Volume()
v.level = 150
print(v.level)   // 100 — silently clamped to the range's upper bound

projectedValue: exposing extra functionality via $

$loggedValue accesses the projectedValue specifically — a separate channel from the wrapped value itself, exactly how SwiftUI's $count exposes a Binding alongside @State's plain Int.

Swiftprojected_value.swift
@propertyWrapper
struct Logged<T> {
    private var value: T
    private(set) var projectedValue: [String] = []   // a log of every value it's held

    var wrappedValue: T {
        get { value }
        set {
            projectedValue.append("\(newValue)")
            value = newValue
        }
    }

    init(wrappedValue: T) {
        self.value = wrappedValue
    }
}

struct Settings {
    @Logged var volume: Int = 50
}

var s = Settings()
s.volume = 75
s.volume = 90
print(s.$volume)   // ["75", "90"] — accessed via $, the projectedValue

Why this matters: the same mechanism as @State/@Published

@State's wrappedValue triggers a SwiftUI view refresh on write, and @Published's wrappedValue notifies Combine subscribers — conceptually identical to the custom Clamped wrapper above, just with different logic inside the same get/set mechanism.

Sources

1
Apple / Swift.org. The Swift Programming Language, "Properties — Property Wrappers," docs.swift.org/swift-book.