- API fetch
- Array
- Async await
- Class
- Closures
- Computed property
- Concurrency
- Constants
- Data types
- Defer statement
- Dictionary
- Enum
- Escaping closure
- Extension
- For loop
- forEach
- Function
- Generics
- Guard statement
- if let statement
- Inheritance
- inout
- Lazy var
- Operator
- Optionals
- Property observers
- Property wrapper
- Protocol
- String formatting
- String interpolation
- Struct
- Switch statement
- Try catch
- Tuple
- Variables
- While loop
SWIFT
Swift Property Wrappers: Syntax, Customization, and Use Cases
Property wrappers in Swift, introduced in version 5.1, let you define reusable behavior for properties. They’re ideal for handling value transformation, validation, caching, or logging—without duplicating logic across multiple places. Wrappers make your code more modular, expressive, and easier to maintain, especially when working with recurring patterns.
What Is a Property Wrapper?
A property wrapper is a type—usually a struct—that encapsulates the logic for reading and writing a property’s value. Instead of placing code inside get
and set
, you delegate that behavior to a reusable wrapper.
Basic Syntax
To create one, define a type with the @propertyWrapper
attribute and include a wrappedValue
property:
@propertyWrapper
struct Clamped {
private var value: Int
private 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)
}
}
You can then apply it to a property:
@Clamped(0...100) var volume: Int = 50
volume = 120
print(volume) // 100
The wrapper enforces the value range automatically.
Why Use Them?
Wrappers help eliminate repetitive logic. Common use cases include:
- Input validation (e.g., clamping, trimming)
- Logging or tracking value changes
- Integrating with UserDefaults
- Lazy or thread-safe initialization
- Data formatting or transformation
Once defined, a wrapper can be reused with a simple @WrapperName
declaration.
Key Components
A minimal property wrapper includes:
- The
@propertyWrapper
annotation - A
wrappedValue
computed property - An initializer that receives the original value
You can optionally expose a projectedValue
with $
syntax or access self
in init
.
Example: Trim Whitespace
This wrapper trims whitespace from assigned strings:
@propertyWrapper
struct Trimmed {
private var value: String = ""
var wrappedValue: String {
get { value }
set { value = newValue.trimmingCharacters(in: .whitespacesAndNewlines) }
}
init(wrappedValue: String) {
self.wrappedValue = wrappedValue
}
}
Usage:
@Trimmed var name: String = " Alice "
print(name) // "Alice"
Cleaner and more consistent than trimming manually every time.
Establishing Default Behavior
Default values passed to a property are forwarded to the wrapper’s initializer:
@propertyWrapper
struct Capitalized {
private var value: String = ""
var wrappedValue: String {
get { value }
set { value = newValue.capitalized }
}
}
Applied like this:
@Capitalized var title: String = "developer"
print(title) // "Developer"
This helps enforce formatting rules throughout your codebase.
Projected Values with $
Syntax
Wrappers can expose additional data through projectedValue
, accessed with a dollar sign:
@propertyWrapper
struct Logged<T> {
private var value: T
var changes: [T] = []
var wrappedValue: T {
get { value }
set {
changes.append(newValue)
value = newValue
}
}
var projectedValue: [T] {
return changes
}
init(wrappedValue: T) {
self.value = wrappedValue
}
}
Usage:
@Logged var score: Int = 0
score = 5
score = 10
print($score) // [5, 10]
This gives you insight into how values have changed over time.
Built-In Use in SwiftUI
SwiftUI heavily relies on wrappers for state management:
@State
: local view state@Binding
: two-way binding between views@Environment
: reads values from the view hierarchy@ObservedObject
and@StateObject
: observable models
These wrappers are fundamental to SwiftUI’s declarative data flow. When you declare @State var isOn = false
, you're tapping into this powerful mechanism.
Combining Multiple Wrappers
You can nest multiple wrappers on the same property. Swift applies them from the inside out:
@propertyWrapper
struct Uppercase {
private var value: String = ""
var wrappedValue: String {
get { value }
set { value = newValue.uppercased() }
}
}
@Uppercase
@Trimmed
var greeting: String = " hello "
print(greeting) // "HELLO"
First, the string is trimmed, then uppercased.
Wrapping Complex Types
Wrappers aren’t limited to primitive types. You can use them with collections, optionals, or custom structs:
@propertyWrapper
struct NonEmptyArray<T> {
private var array: [T]
var wrappedValue: [T] {
get { array }
set { array = newValue.isEmpty ? [T]() : newValue }
}
init(wrappedValue: [T]) {
self.array = wrappedValue
}
}
Usage:
@NonEmptyArray var items: [Int] = []
This ensures items
will never remain empty unintentionally.
Limitations to Keep in Mind
While wrappers are powerful, they come with a few restrictions:
- They can’t be applied to computed properties
- Not supported on global variables
- May obscure logic if overused
- Debugging can be slightly harder due to indirection
- You must manually handle
Codable
compliance for wrapped properties
Use them for clean encapsulation—not to hide complexity or introduce side effects.
Codable Support
To make your wrapper compatible with Codable
, you’ll need to add conformance manually:
@propertyWrapper
struct Uppercase: Codable {
var wrappedValue: String
init(wrappedValue: String) {
self.wrappedValue = wrappedValue.uppercased()
}
}
Be sure to implement custom encoding/decoding logic in the containing type if necessary.
Summary
Property wrappers let you define and reuse custom logic for property behavior in a clean, declarative way. With tools like wrappedValue
, projectedValue
, and flexible initializers, they enable you to handle transformation, validation, and tracking consistently across your codebase.
They're essential in SwiftUI and valuable anywhere modular, reusable design matters. Whether you're building lightweight utilities or powerful abstractions, mastering property wrappers gives you another layer of control and clarity in modern Swift development.
Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.