- 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 Wrapper: Syntax, Customization, and Use Cases
A Swift property wrapper
is a powerful feature introduced in Swift 5.1 that lets developers define reusable behavior for properties. With this mechanism, you can add functionality like value transformation, logging, caching, or validation to properties without duplicating code. A property wrapper Swift
makes your code more expressive, modular, and maintainable—especially when working with repetitive logic across multiple properties.
Understanding the Basics of Swift Property Wrappers
A property wrapper is essentially a struct, class, or enum that encapsulates logic for reading and writing a value. Instead of writing code directly inside a property’s get
and set
, you apply a wrapper that handles it for you.
Basic Syntax
To declare a wrapper, define a type marked with the @propertyWrapper
attribute and implement 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)
}
}
Then apply it to any property like this:
@Clamped(0...100) var volume: Int = 50
Assigning a value outside the specified range automatically clamps it:
volume = 120
print(volume) // Outputs: 100
Why Use Property Wrappers in Swift
The purpose of a Swift property wrapper
is to eliminate repetitive logic. Common reasons to use wrappers include:
- Input validation (e.g., clamping numbers, trimming strings)
- Synchronizing with user defaults
- Logging changes
- Lazy initialization
- Injecting behavior like thread safety or caching
You can encapsulate all this behavior and apply it across properties just by writing @WrapperName
.
Components of a Property Wrapper Swift Supports
At minimum, you need:
@propertyWrapper
annotation- A
wrappedValue
computed property - An initializer
You can also expose additional behaviors with projectedValue
or access to self
within init
.
Example: Trimming Whitespace with a Custom Wrapper
Here's a simple Swift custom property wrapper
that 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) // Outputs: "Alice"
This is cleaner than writing .trimmingCharacters
every time you assign a value.
Using Default Values in Property Wrappers
The initial value passed during property declaration is forwarded to the wrapper's init(wrappedValue:)
. You can leverage this to establish default behavior:
@propertyWrapper
struct Capitalized {
private var value: String = ""
var wrappedValue: String {
get { value }
set { value = newValue.capitalized }
}
}
Then:
@Capitalized var title: String = "manager"
print(title) // Outputs: "Manager"
This avoids repeating .capitalized
logic throughout the codebase.
Accessing the Projected Value
Property wrappers can also expose an additional value using the $
prefix via projectedValue
.
@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
}
}
Use it like this:
@Logged var score: Int = 0
score = 5
score = 10
print($score) // [5, 10]
$score
gives you access to the history of changes, separate from the current value.
Property Wrappers in SwiftUI
SwiftUI makes extensive use of property wrappers like:
@State
: for local view state@Binding
: to create two-way bindings@Environment
: to read values from the view hierarchy@ObservedObject
and@StateObject
: for observing model changes
These wrappers are foundational for the SwiftUI data flow model. When you declare @State var isActive = false
, you’re using a system-defined Swift property wrapper
behind the scenes to track and update the view state.
Nesting Property Wrappers
You can use multiple property wrappers on the same property, but it requires careful handling. 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) // Outputs: "HELLO"
In this example, Trimmed
is applied first, then Uppercase
.
Wrapping Non-Primitive Types
A property wrapper Swift
pattern isn't limited to strings or integers. You can wrap custom structs, arrays, dictionaries, or even optionals.
@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
}
}
Use it to ensure arrays never stay empty:
@NonEmptyArray var items: [Int] = []
Limitations of Property Wrappers
While powerful, Swift property wrappers
come with some constraints:
- Wrappers can't be applied to computed properties
- They don’t work on global variables
- Wrappers can add indirection, making debugging slightly harder
Codable
compliance for wrapped properties requires manual implementation- Wrappers are executed at runtime, not compile time
You should use them for behavior reuse and encapsulation—not to hide complex logic or side effects.
Making Wrappers Codable-Compatible
To make your custom Swift property wrapper
codable, you need to implement Encodable
and Decodable
conformance manually. Swift doesn't automatically synthesize encoding for wrappedValue
.
Example:
@propertyWrapper
struct Uppercase: Codable {
var wrappedValue: String
init(wrappedValue: String) {
self.wrappedValue = wrappedValue.uppercased()
}
}
Then, the containing struct must implement its own Codable
logic if needed.
Summary
A Swift property wrapper
lets you add reusable behavior to properties in a clean and declarative way. With features like wrappedValue
, projectedValue
, and custom initializers, wrappers encapsulate logic like value transformation, validation, state tracking, or synchronization. Using a property wrapper Swift
developers can avoid duplication and improve maintainability while writing expressive and readable code.
Mastering property wrappers is essential for modern Swift development, especially in SwiftUI and codebases that prioritize modular design. Whether you're implementing lightweight transformations or powerful abstractions, custom wrappers are a flexible tool worth having in your Swift toolkit.
Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.