SwiftQA Random

What are property wrappers and how have you used them in production?

What property wrappers are

A property wrapper is a type that encapsulates storage/behavior around a property’s get/set. You define it once with @propertyWrapper, and applying @YourWrapper to a property generates boilerplate under the hood.

@propertyWrapper
struct Clamped<Value: Comparable> {
    private var value: Value
    private 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) }
    }
}

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

The compiler transforms @Clamped(0...100) var level: Int = 50 into a hidden stored property of type Clamped<Int> plus a computed property level forwarding to wrappedValue.

projectedValue — the $ prefix

Wrappers can expose a secondary value via $propertyName, used heavily by SwiftUI.

The ones you’ll actually use in production

@State / @Binding / @Observable / @Published — solve “when this value changes, the view needs to re-render” without manual subscription management.

@AppStorage / @SceneStorage — wraps UserDefaults reads/writes so a property behaves like a normal variable but persists automatically:

@AppStorage("hasSeenOnboarding") var hasSeenOnboarding: Bool = false

Custom validation/sanitization wrappers — for form input or model validation.

Dependency injection wrappers — a @Injected wrapper around a service locator/container. Worth knowing the tradeoff: this trades explicit constructor injection (testable, visible dependencies) for convenience — some senior engineers consider @Injected-style DI an anti-pattern precisely because it hides dependencies and makes unit testing harder.

How I’d frame an answer if this were your interview

“We had a form-heavy screen with a dozen text fields, each needing trimming, max-length enforcement, and validation state exposed to the view. We wrote a @FormField wrapper that handled trimming/validation internally and exposed $fieldName as the validation state. It cut a lot of repeated boilerplate, but the tradeoff was debuggability — stepping through in the debugger was less direct since you’re jumping into the wrapper’s get/set. I’d reach for it again for cross-cutting concerns like validation or persistence, but wouldn’t wrap something with truly one-off logic.”

That structure — specific problem, what the wrapper actually solved, honest cost, when you would/wouldn’t reuse the pattern — is what separates “I know property wrappers exist” from “I’ve actually made an engineering tradeoff with them.”