← Back to Swift Course | Chapter 12: Generics & Advanced Types | Lesson 6 of 7

Property Wrappers

A property wrapper is reusable magic you attach above a property to automatically add extra behavior every time it's read or written.
Syntax
swift
@propertyWrapper
struct WrapperName {
    var wrappedValue: Type {
        get { /* return stored value */ }
        set { /* validate, then store */ }
    }
}

struct Model {
    @WrapperName var property: Type
}

Defining a Simple Property Wrapper

A property wrapper struct implements wrappedValue, and any logic placed inside its getter and setter runs automatically whenever the wrapped property is accessed.

Example: Defining a Simple Property Wrapper

markup
@propertyWrapper
// Define a struct `Clamped`
struct Clamped {
    private var value: Int
    private let range: ClosedRange<Int>
    init(wrappedValue: Int, _ range: ClosedRange<Int>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }
    var wrappedValue: Int {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }
}
// Define a struct `Settings`
struct Settings {
    @Clamped(0...100) var volume: Int = 50
}
// Declare a mutable variable `settings`, initialized to `Settings()`
var settings = Settings()
settings.volume = 150
// Print `settings.volume` to the console
print(settings.volume)

Using a Property Wrapper on Multiple Properties

Once defined, a property wrapper can be reused on any number of properties across different types, applying its behavior consistently everywhere.

Example: Using a Property Wrapper on Multiple Properties

markup
@propertyWrapper
// Define a struct `Clamped`
struct Clamped {
    private var value: Int
    private let range: ClosedRange<Int>
    init(wrappedValue: Int, _ range: ClosedRange<Int>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }
    var wrappedValue: Int {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }
}
// Define a struct `GameCharacter`
struct GameCharacter {
    @Clamped(0...100) var health: Int = 100
    @Clamped(0...10) var level: Int = 1
}
// Declare a mutable variable `hero`, initialized to `GameCharacter()`
var hero = GameCharacter()
hero.health = -20
hero.level = 99
// Print "Health: \(hero.health), Level: \(hero.level)" to the console
print("Health: \(hero.health), Level: \(hero.level)")
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Forgetting a property wrapper type must declare a wrappedValue property; that's the required contract the @ attribute relies on.
  2. Confusing the wrapped value (accessed normally by name) with the wrapper instance itself (accessed with a leading $, called the projected value).
  3. Applying a property wrapper to a computed property; wrappers only apply to stored properties.
Chapter Summary
  • @propertyWrapper marks a struct or class as a reusable property wrapper.
  • The wrapper must expose a wrappedValue property that Swift uses transparently.
  • Applying @WrapperName above a property automatically routes access through the wrapper.
  • A projectedValue (accessed with $name) can expose the wrapper itself for more advanced use.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.