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
@FormFieldwrapper that handled trimming/validation internally and exposed$fieldNameas 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.”